Decouple Authorization subsystem from Legacy API. (#52638)

This commit is contained in:
Aleh Zasypkin 2019-12-11 08:55:46 +01:00 committed by GitHub
parent f0eb4bb675
commit 7e27f0d35f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 37 additions and 37 deletions

View file

@ -132,7 +132,6 @@ export const security = (kibana) => new kibana.Plugin({
server.plugins.kibana.systemApi
),
cspRules: createCSPRuleString(config.get('csp.rules')),
kibanaIndexName: config.get('kibana.index'),
});
// Legacy xPack Info endpoint returns whatever we return in a callback for `registerLicenseCheckResultsGenerator`

View file

@ -48,7 +48,7 @@ describe('#atSpace', () => {
const checkPrivilegesWithRequest = checkPrivilegesWithRequestFactory(
mockActions,
mockClusterClient,
() => application
application
);
const request = httpServerMock.createKibanaRequest();
const checkPrivileges = checkPrivilegesWithRequest(request);
@ -291,7 +291,7 @@ describe('#atSpaces', () => {
const checkPrivilegesWithRequest = checkPrivilegesWithRequestFactory(
mockActions,
mockClusterClient,
() => application
application
);
const request = httpServerMock.createKibanaRequest();
const checkPrivileges = checkPrivilegesWithRequest(request);
@ -772,7 +772,7 @@ describe('#globally', () => {
const checkPrivilegesWithRequest = checkPrivilegesWithRequestFactory(
mockActions,
mockClusterClient,
() => application
application
);
const request = httpServerMock.createKibanaRequest();
const checkPrivileges = checkPrivilegesWithRequest(request);

View file

@ -61,7 +61,7 @@ export interface CheckPrivileges {
export function checkPrivilegesWithRequestFactory(
actions: CheckPrivilegesActions,
clusterClient: IClusterClient,
getApplicationName: () => string
applicationName: string
) {
const hasIncompatibleVersion = (
applicationPrivilegesResponse: HasPrivilegesResponseApplication
@ -81,23 +81,24 @@ export function checkPrivilegesWithRequestFactory(
: [privilegeOrPrivileges];
const allApplicationPrivileges = uniq([actions.version, actions.login, ...privileges]);
const application = getApplicationName();
const hasPrivilegesResponse = (await clusterClient
.asScoped(request)
.callAsCurrentUser('shield.hasPrivileges', {
body: {
applications: [{ application, resources, privileges: allApplicationPrivileges }],
applications: [
{ application: applicationName, resources, privileges: allApplicationPrivileges },
],
},
})) as HasPrivilegesResponse;
validateEsPrivilegeResponse(
hasPrivilegesResponse,
application,
applicationName,
allApplicationPrivileges,
resources
);
const applicationPrivilegesResponse = hasPrivilegesResponse.application[application];
const applicationPrivilegesResponse = hasPrivilegesResponse.application[applicationName];
if (hasIncompatibleVersion(applicationPrivilegesResponse)) {
throw new Error(

View file

@ -8,12 +8,15 @@ import { Actions } from '.';
import { AuthorizationMode } from './mode';
export const authorizationMock = {
create: ({ version = 'mock-version' }: { version?: string } = {}) => ({
create: ({
version = 'mock-version',
applicationName = 'mock-application',
}: { version?: string; applicationName?: string } = {}) => ({
actions: new Actions(version),
checkPrivilegesWithRequest: jest.fn(),
checkPrivilegesDynamicallyWithRequest: jest.fn(),
checkSavedObjectsPrivilegesWithRequest: jest.fn(),
getApplicationName: jest.fn().mockReturnValue('mock-application'),
applicationName,
mode: { useRbacForRequest: jest.fn() } as jest.Mocked<AuthorizationMode>,
privileges: { get: jest.fn() },
registerPrivilegesWithCluster: jest.fn(),

View file

@ -53,7 +53,6 @@ test(`returns exposed services`, () => {
.fn()
.mockReturnValue({ getSpaceId: jest.fn(), namespaceToSpaceId: jest.fn() });
const mockFeaturesService = { getFeatures: () => [] };
const mockGetLegacyAPI = () => ({ kibanaIndexName });
const mockLicense = licenseMock.create();
const authz = setupAuthorization({
@ -61,20 +60,20 @@ test(`returns exposed services`, () => {
clusterClient: mockClusterClient,
license: mockLicense,
loggers: loggingServiceMock.create(),
getLegacyAPI: mockGetLegacyAPI,
kibanaIndexName,
packageVersion: 'some-version',
featuresService: mockFeaturesService,
getSpacesService: mockGetSpacesService,
});
expect(authz.actions.version).toBe('version:some-version');
expect(authz.getApplicationName()).toBe(application);
expect(authz.applicationName).toBe(application);
expect(authz.checkPrivilegesWithRequest).toBe(mockCheckPrivilegesWithRequest);
expect(checkPrivilegesWithRequestFactory).toHaveBeenCalledWith(
authz.actions,
mockClusterClient,
authz.getApplicationName
authz.applicationName
);
expect(authz.checkPrivilegesDynamicallyWithRequest).toBe(

View file

@ -12,7 +12,7 @@ import {
IClusterClient,
} from '../../../../../src/core/server';
import { FeaturesService, LegacyAPI, SpacesService } from '../plugin';
import { FeaturesService, SpacesService } from '../plugin';
import { Actions } from './actions';
import { CheckPrivilegesWithRequest, checkPrivilegesWithRequestFactory } from './check_privileges';
import {
@ -43,7 +43,7 @@ interface SetupAuthorizationParams {
license: SecurityLicense;
loggers: LoggerFactory;
featuresService: FeaturesService;
getLegacyAPI(): Pick<LegacyAPI, 'kibanaIndexName'>;
kibanaIndexName: string;
getSpacesService(): SpacesService | undefined;
}
@ -52,7 +52,7 @@ export interface Authorization {
checkPrivilegesWithRequest: CheckPrivilegesWithRequest;
checkPrivilegesDynamicallyWithRequest: CheckPrivilegesDynamicallyWithRequest;
checkSavedObjectsPrivilegesWithRequest: CheckSavedObjectsPrivilegesWithRequest;
getApplicationName: () => string;
applicationName: string;
mode: AuthorizationMode;
privileges: PrivilegesService;
disableUnauthorizedCapabilities: (
@ -69,23 +69,23 @@ export function setupAuthorization({
license,
loggers,
featuresService,
getLegacyAPI,
kibanaIndexName,
getSpacesService,
}: SetupAuthorizationParams): Authorization {
const actions = new Actions(packageVersion);
const mode = authorizationModeFactory(license);
const getApplicationName = () => `${APPLICATION_PREFIX}${getLegacyAPI().kibanaIndexName}`;
const applicationName = `${APPLICATION_PREFIX}${kibanaIndexName}`;
const checkPrivilegesWithRequest = checkPrivilegesWithRequestFactory(
actions,
clusterClient,
getApplicationName
applicationName
);
const privileges = privilegesFactory(actions, featuresService);
const logger = loggers.get('authorization');
const authz = {
actions,
getApplicationName,
applicationName,
checkPrivilegesWithRequest,
checkPrivilegesDynamicallyWithRequest: checkPrivilegesDynamicallyWithRequestFactory(
checkPrivilegesWithRequest,
@ -123,7 +123,7 @@ export function setupAuthorization({
registerPrivilegesWithCluster: async () => {
validateFeaturePrivileges(actions, featuresService.getFeatures());
await registerPrivilegesWithCluster(logger, privileges, getApplicationName(), clusterClient);
await registerPrivilegesWithCluster(logger, privileges, applicationName, clusterClient);
},
};

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Subscription } from 'rxjs';
import { Subscription, combineLatest } from 'rxjs';
import { first } from 'rxjs/operators';
import {
IClusterClient,
@ -42,7 +42,6 @@ export type FeaturesService = Pick<FeaturesSetupContract, 'getFeatures'>;
*/
export interface LegacyAPI {
isSystemAPIRequest: (request: KibanaRequest) => boolean;
kibanaIndexName: string;
cspRules: string;
savedObjects: SavedObjectsLegacyService<KibanaRequest | LegacyRequest>;
auditLogger: {
@ -121,7 +120,10 @@ export class Plugin {
core: CoreSetup,
{ features, licensing }: PluginSetupDependencies
): Promise<RecursiveReadonly<PluginSetupContract>> {
const config = await createConfig$(this.initializerContext, core.http.isTlsEnabled)
const [config, legacyConfig] = await combineLatest([
createConfig$(this.initializerContext, core.http.isTlsEnabled),
this.initializerContext.config.legacy.globalConfig$,
])
.pipe(first())
.toPromise();
@ -148,7 +150,7 @@ export class Plugin {
clusterClient: this.clusterClient,
license,
loggers: this.initializerContext.logger,
getLegacyAPI: this.getLegacyAPI,
kibanaIndexName: legacyConfig.kibana.index,
packageVersion: this.initializerContext.env.packageInfo.version,
getSpacesService: this.getSpacesService,
featuresService: features,

View file

@ -36,7 +36,7 @@ describe('GET role', () => {
) => {
test(description, async () => {
const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
mockRouteDefinitionParams.authz.getApplicationName.mockReturnValue(application);
mockRouteDefinitionParams.authz.applicationName = application;
const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient();
mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient);

View file

@ -28,7 +28,7 @@ export function defineGetRolesRoutes({ router, authz, clusterClient }: RouteDefi
body: transformElasticsearchRoleToRole(
elasticsearchRole,
request.params.name,
authz.getApplicationName()
authz.applicationName
),
});
}

View file

@ -31,7 +31,7 @@ describe('GET all roles', () => {
) => {
test(description, async () => {
const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
mockRouteDefinitionParams.authz.getApplicationName.mockReturnValue(application);
mockRouteDefinitionParams.authz.applicationName = application;
const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient();
mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(mockScopedClusterClient);

View file

@ -22,11 +22,7 @@ export function defineGetAllRolesRoutes({ router, authz, clusterClient }: RouteD
return response.ok({
body: Object.entries(elasticsearchRoles)
.map(([roleName, elasticsearchRole]) =>
transformElasticsearchRoleToRole(
elasticsearchRole,
roleName,
authz.getApplicationName()
)
transformElasticsearchRoleToRole(elasticsearchRole, roleName, authz.applicationName)
)
.sort((roleA, roleB) => {
if (roleA.name < roleB.name) {

View file

@ -62,7 +62,7 @@ const putRoleTest = (
) => {
test(description, async () => {
const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
mockRouteDefinitionParams.authz.getApplicationName.mockReturnValue(application);
mockRouteDefinitionParams.authz.applicationName = application;
mockRouteDefinitionParams.authz.privileges.get.mockReturnValue(privilegeMap);
const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient();

View file

@ -42,7 +42,7 @@ export function definePutRolesRoutes({ router, authz, clusterClient }: RouteDefi
const body = transformPutPayloadToElasticsearchRole(
request.body,
authz.getApplicationName(),
authz.applicationName,
rawRoles[name] ? rawRoles[name].applications : []
);