Skip to content

Commit

Permalink
[Fleet] Update calculateAuthz calls to respect superuser for now (#11…
Browse files Browse the repository at this point in the history
  • Loading branch information
joshdover authored Dec 2, 2021
1 parent 4107d40 commit 42e2e78
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 75 deletions.
4 changes: 2 additions & 2 deletions x-pack/plugins/fleet/public/mock/plugin_interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const createStartMock = (extensionsStorage: UIExtensionsStorage = {}): Mo
return {
isInitialized: jest.fn().mockResolvedValue(true),
registerExtension: createExtensionRegistrationCallback(extensionsStorage),
authz: {
authz: Promise.resolve({
fleet: {
all: true,
setup: true,
Expand All @@ -31,6 +31,6 @@ export const createStartMock = (extensionsStorage: UIExtensionsStorage = {}): Mo
readIntegrationPolicies: true,
writeIntegrationPolicies: true,
},
},
}),
};
};
3 changes: 2 additions & 1 deletion x-pack/plugins/fleet/public/mock/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export type MockedFleetSetupDeps = MockedKeys<FleetSetupDeps>;

export type MockedFleetStartDeps = MockedKeys<FleetStartDeps>;

export type MockedFleetStart = MockedKeys<FleetStart>;
// Don't wrap the `authz` property which is a promise with `jest.Mocked`
export type MockedFleetStart = MockedKeys<Omit<FleetStart, 'authz'>> & Pick<FleetStart, 'authz'>;
87 changes: 47 additions & 40 deletions x-pack/plugins/fleet/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import type {

import type { SharePluginStart } from 'src/plugins/share/public';

import { once } from 'lodash';

import type { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public';

import { DEFAULT_APP_CATEGORIES, AppNavLinkStatus } from '../../../../src/core/public';
Expand Down Expand Up @@ -72,7 +74,7 @@ export interface FleetSetup {}
*/
export interface FleetStart {
/** Authorization for the current user */
authz: FleetAuthz;
authz: Promise<FleetAuthz>;
registerExtension: UIExtensionRegistrationCallback;
isInitialized: () => Promise<true>;
}
Expand Down Expand Up @@ -144,7 +146,7 @@ export class FleetPlugin implements Plugin<FleetSetup, FleetStart, FleetSetupDep
...startDepsServices,
storage: this.storage,
cloud: deps.cloud,
authz: fleetStart.authz,
authz: await fleetStart.authz,
};
const { renderApp, teardownIntegrations } = await import('./applications/integrations');

Expand Down Expand Up @@ -181,7 +183,7 @@ export class FleetPlugin implements Plugin<FleetSetup, FleetStart, FleetSetupDep
...startDepsServices,
storage: this.storage,
cloud: deps.cloud,
authz: fleetStart.authz,
authz: await fleetStart.authz,
};
const { renderApp, teardownFleet } = await import('./applications/fleet');
const unmount = renderApp(startServices, params, config, kibanaVersion, extensions);
Expand Down Expand Up @@ -237,55 +239,60 @@ export class FleetPlugin implements Plugin<FleetSetup, FleetStart, FleetSetupDep
}

public start(core: CoreStart): FleetStart {
let successPromise: ReturnType<FleetStart['isInitialized']>;
const registerExtension = createExtensionRegistrationCallback(this.extensions);
const getPermissions = once(() =>
core.http.get<CheckPermissionsResponse>(appRoutesService.getCheckPermissionsPath())
);

registerExtension({
package: CUSTOM_LOGS_INTEGRATION_NAME,
view: 'package-detail-assets',
Component: LazyCustomLogsAssetsExtension,
});

const { capabilities } = core.application;
const authz = calculateAuthz({
fleet: {
// Once we have a split privilege, this should be using fleetv2
// all: capabilities.fleetv2.all as boolean,
all: capabilities.fleet.all as boolean,
setup: false, // browser users will never have setup privileges
},
return {
// Temporarily rely on superuser check to calculate authz. Once Kibana RBAC is in place for Fleet this should
// switch to a sync calculation based on `core.application.capabilites` properties.
authz: getPermissions()
.catch((e) => {
// eslint-disable-next-line no-console
console.warn(`Could not load Fleet permissions due to error: ${e}`);
return { success: false };
})
.then((permissionsResponse) => {
if (permissionsResponse.success) {
// If superuser, give access to everything
return calculateAuthz({
fleet: { all: true, setup: true },
integrations: { all: true, read: true },
});
} else {
// All other users only get access to read integrations if they have the read privilege
const { capabilities } = core.application;
return calculateAuthz({
fleet: { all: false, setup: false },
integrations: { all: false, read: capabilities.fleet.read as boolean },
});
}
}),

integrations: {
all: capabilities.fleet.all as boolean,
read: capabilities.fleet.read as boolean,
},
});
isInitialized: once(async () => {
const permissionsResponse = await getPermissions();

return {
authz,
isInitialized: () => {
if (!successPromise) {
successPromise = Promise.resolve().then(async () => {
const permissionsResponse = await core.http.get<CheckPermissionsResponse>(
appRoutesService.getCheckPermissionsPath()
);

if (permissionsResponse?.success) {
return core.http
.post<PostFleetSetupResponse>(setupRouteService.getSetupPath())
.then(({ isInitialized }) =>
isInitialized
? Promise.resolve(true)
: Promise.reject(new Error('Unknown setup error'))
);
} else {
throw new Error(permissionsResponse?.error || 'Unknown permissions error');
}
});
if (permissionsResponse?.success) {
const { isInitialized } = await core.http.post<PostFleetSetupResponse>(
setupRouteService.getSetupPath()
);
if (!isInitialized) {
throw new Error('Unknown setup error');
}

return true;
} else {
throw new Error(permissionsResponse?.error || 'Unknown permissions error');
}
}),

return successPromise;
},
registerExtension,
};
}
Expand Down
62 changes: 30 additions & 32 deletions x-pack/plugins/fleet/server/routes/security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,41 +127,39 @@ export async function getAuthzFromRequest(req: KibanaRequest): Promise<FleetAuth
const security = appContextService.getSecurity();

if (security.authz.mode.useRbacForRequest(req)) {
const checkPrivileges = security.authz.checkPrivilegesDynamicallyWithRequest(req);
const { privileges } = await checkPrivileges({
kibana: [
security.authz.actions.api.get('fleet-all'),
security.authz.actions.api.get('fleet-setup'),
security.authz.actions.api.get('integrations-all'),
security.authz.actions.api.get('integrations-read'),
],
});

const [fleetAll, fleetSetup, intAll, intRead] = privileges.kibana;

return calculateAuthz({
fleet: {
all: fleetAll.authorized,
setup: fleetSetup.authorized,
},

integrations: {
all: intAll.authorized,
read: intRead.authorized,
},
});
if (checkSuperuser(req)) {
// Superusers get access to everything
// Once we implement Kibana RBAC, remove this and use `checkPrivileges` exclusively
return calculateAuthz({
fleet: { all: true, setup: true },
integrations: { all: true, read: true },
});
} else if (await checkFleetSetupPrivilege(req)) {
// fleet-setup privilege only gets access to setup actions
return calculateAuthz({
fleet: { all: false, setup: true },
integrations: { all: false, read: false },
});
} else {
// All other users only get access to read integrations if they have the read privilege
const checkPrivileges = security.authz.checkPrivilegesDynamicallyWithRequest(req);
const { privileges } = await checkPrivileges({
kibana: [security.authz.actions.api.get('integrations-read')],
});

const [intRead] = privileges.kibana;

// Once we implement Kibana RBAC, use `checkPrivileges` for all privileges instead of only integrations.read
return calculateAuthz({
fleet: { all: true, setup: true },
integrations: { all: true, read: intRead.authorized },
});
}
}

return calculateAuthz({
fleet: {
all: true,
setup: true,
},

integrations: {
all: true,
read: true,
},
fleet: { all: false, setup: false },
integrations: { all: false, read: false },
});
}

Expand Down

0 comments on commit 42e2e78

Please sign in to comment.