Skip to content

Commit

Permalink
Merge pull request #14 from eokoneyo/assign-roles-to-space
Browse files Browse the repository at this point in the history
Further improvements to spaces role assignment tab
  • Loading branch information
tsullivan authored Aug 28, 2024
2 parents ae18346 + 18c0d83 commit ccd34f3
Show file tree
Hide file tree
Showing 34 changed files with 1,863 additions and 599 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface PrivilegesAPIClientGetAllArgs {
*/
respectLicenseLevel: boolean;
}
// TODO: Eyo include the proper return types for contract

export abstract class PrivilegesAPIClientPublicContract {
abstract getAll(args: PrivilegesAPIClientGetAllArgs): Promise<RawKibanaPrivileges>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export interface RolesAPIClient {
getRole: (roleName: string) => Promise<Role>;
deleteRole: (roleName: string) => Promise<void>;
saveRole: (payload: RolePutPayload) => Promise<void>;
bulkUpdateRoles: (payload: { rolesUpdate: Role[] }) => Promise<void>;
}
2 changes: 1 addition & 1 deletion x-pack/packages/security/plugin_types_public/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
"@kbn/core-user-profile-common",
"@kbn/security-plugin-types-common",
"@kbn/core-security-common",
"@kbn/security-authorization-core"
"@kbn/security-authorization-core",
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import {
kibanaFeatures,
} from '@kbn/security-role-management-model/src/__fixtures__';
import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers';
import type { Role } from '@kbn/security-plugin-types-common';

import { getDisplayedFeaturePrivileges } from './__fixtures__';
import { FeatureTable } from './feature_table';
import type { Role } from '@kbn/security-plugin-types-common';
import { PrivilegeFormCalculator } from '../privilege_form_calculator';

const createRole = (kibana: Role['kibana'] = []): Role => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import {
createKibanaPrivileges,
kibanaFeatures,
} from '@kbn/security-role-management-model/src/__fixtures__';
import type { Role } from '@kbn/security-plugin-types-common';
import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers';

import { FeatureTableExpandedRow } from './feature_table_expanded_row';
import type { Role } from '@kbn/security-plugin-types-common';
import { PrivilegeFormCalculator } from '../privilege_form_calculator';

const createRole = (kibana: Role['kibana'] = []): Role => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import type {
SubFeaturePrivilege,
SubFeaturePrivilegeGroup,
} from '@kbn/security-role-management-model';

import { NO_PRIVILEGE_VALUE } from '../constants';
import type { PrivilegeFormCalculator } from '../privilege_form_calculator';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
createKibanaPrivileges,
kibanaFeatures,
} from '@kbn/security-role-management-model/src/__fixtures__';
import type { Role } from '@kbn/security-plugin-types-common';

import { PrivilegeFormCalculator } from './privilege_form_calculator';
import type { Role } from '@kbn/security-plugin-types-common';

const createRole = (kibana: Role['kibana'] = []): Role => {
return {
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/security/public/authentication/index.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const authorizationMock = {
getRole: jest.fn(),
deleteRole: jest.fn(),
saveRole: jest.fn(),
bulkUpdateRoles: jest.fn(),
},
privileges: {
getAll: jest.fn(),
Expand All @@ -43,6 +44,7 @@ export const authorizationMock = {
getRole: jest.fn(),
deleteRole: jest.fn(),
saveRole: jest.fn(),
bulkUpdateRoles: jest.fn(),
},
privileges: {
getAll: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class AuthorizationService {
getRole: rolesAPIClient.getRole,
deleteRole: rolesAPIClient.deleteRole,
saveRole: rolesAPIClient.saveRole,
bulkUpdateRoles: rolesAPIClient.bulkUpdateRoles,
},
privileges: {
getAll: privilegesAPIClient.getAll.bind(privilegesAPIClient),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export const rolesAPIClientMock = {
getRole: jest.fn(),
deleteRole: jest.fn(),
saveRole: jest.fn(),
bulkUpdateRoles: jest.fn(),
}),
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ export class RolesAPIClient {
});
};

public bulkUpdateRoles = async ({ rolesUpdate }: { rolesUpdate: Role[] }) => {
await this.http.post('/api/security/roles', {
body: JSON.stringify({
roles: rolesUpdate.reduce((transformed, value) => {
transformed[value.name] = this.transformRoleForSave(copyRole(value));
return transformed;
}, {} as Record<string, ReturnType<typeof this.transformRoleForSave>>),
}),
});
};

private transformRoleForSave = (role: Role) => {
// Remove any placeholder index privileges
const isPlaceholderPrivilege = (
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/security/public/plugin.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ describe('Security Plugin', () => {
"getAll": [Function],
},
"roles": Object {
"bulkUpdateRoles": [Function],
"deleteRole": [Function],
"getRole": [Function],
"getRoles": [Function],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { PrivilegesAPIClientPublicContract } from '@kbn/security-plugin-types-public';

export const createPrivilegeAPIClientMock = (): PrivilegesAPIClientPublicContract => {
return {
getAll: jest.fn(),
};
};

export const getPrivilegeAPIClientMock = jest
.fn()
.mockResolvedValue(createPrivilegeAPIClientMock());
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const createRolesAPIClientMock = (): RolesAPIClient => {
getRole: jest.fn(),
saveRole: jest.fn(),
deleteRole: jest.fn(),
bulkUpdateRoles: jest.fn(),
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ describe('spacesManagementApp', () => {
css="You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop)."
data-test-subj="kbnRedirectAppLink"
>
Spaces View Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"serverBasePath":"","http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"overlays":{"banners":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}},"allowFeatureVisibility":true,"allowSolutionVisibility":true}
Spaces View Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"serverBasePath":"","http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"overlays":{"banners":{}},"notifications":{"toasts":{}},"theme":{"theme$":{}},"i18n":{},"spacesManager":{"onActiveSpaceChange$":{}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}},"allowFeatureVisibility":true,"allowSolutionVisibility":true}
</div>
</div>
`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const spacesManagementApp = Object.freeze({
config,
eventTracker,
getRolesAPIClient,
getPrivilegesAPIClient,
}: CreateParams) {
const title = i18n.translate('xpack.spaces.displayName', {
defaultMessage: 'Spaces',
Expand Down Expand Up @@ -71,7 +72,7 @@ export const spacesManagementApp = Object.freeze({
text: title,
href: `/`,
};
const { notifications, application, chrome, http, overlays } = coreStart;
const { notifications, application, chrome, http, overlays, theme } = coreStart;

chrome.docTitle.change(title);

Expand Down Expand Up @@ -147,6 +148,8 @@ export const spacesManagementApp = Object.freeze({
http={http}
overlays={overlays}
notifications={notifications}
theme={theme}
i18n={coreStart.i18n}
spacesManager={spacesManager}
spaceId={spaceId}
onLoadSpace={onLoadSpace}
Expand All @@ -155,6 +158,7 @@ export const spacesManagementApp = Object.freeze({
getRolesAPIClient={getRolesAPIClient}
allowFeatureVisibility={config.allowFeatureVisibility}
allowSolutionVisibility={config.allowSolutionVisibility}
getPrivilegesAPIClient={getPrivilegesAPIClient}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { KibanaFeature } from '@kbn/features-plugin/public';
import type { Space } from '../../../../common';
import { getTabs, type GetTabsProps, type ViewSpaceTab } from '../view_space_tabs';

type UseTabsProps = Pick<GetTabsProps, 'roles' | 'capabilities'> & {
type UseTabsProps = Pick<GetTabsProps, 'capabilities' | 'rolesCount'> & {
space: Space | null;
features: KibanaFeature[] | null;
currentSelectedTabId: string;
Expand Down

This file was deleted.

40 changes: 40 additions & 0 deletions x-pack/plugins/spaces/public/management/view_space/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import type { ComponentProps, PropsWithChildren } from 'react';

import { ViewSpaceProvider, type ViewSpaceProviderProps } from './provider';
import { ViewSpace } from './view_space';

type ViewSpacePageProps = ComponentProps<typeof ViewSpace> & ViewSpaceProviderProps;

export function ViewSpacePage({
spaceId,
getFeatures,
history,
onLoadSpace,
selectedTabId,
allowFeatureVisibility,
allowSolutionVisibility,
children,
...viewSpaceServicesProps
}: PropsWithChildren<ViewSpacePageProps>) {
return (
<ViewSpaceProvider {...viewSpaceServicesProps}>
<ViewSpace
spaceId={spaceId}
getFeatures={getFeatures}
history={history}
onLoadSpace={onLoadSpace}
selectedTabId={selectedTabId}
allowFeatureVisibility={allowFeatureVisibility}
allowSolutionVisibility={allowSolutionVisibility}
/>
</ViewSpaceProvider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@
* 2.0.
*/

export { ViewSpacePage } from './view_space';
export { ViewSpaceProvider, useViewSpaceServices, useViewSpaceStore } from './view_space_provider';
export type {
ViewSpaceProviderProps,
ViewSpaceServices,
ViewSpaceStore,
} from './view_space_provider';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { type Reducer } from 'react';

import type { Role } from '@kbn/security-plugin-types-common';

export type IDispatchAction =
| {
/** @description updates a single role record */
type: 'update_roles' | 'remove_roles';
payload: Role[];
}
| {
type: 'string';
payload: any;
};

export interface IViewSpaceStoreState {
/** roles assigned to current space */
roles: Map<Role['name'], Role>;
}

export const createSpaceRolesReducer: Reducer<IViewSpaceStoreState, IDispatchAction> = (
state,
action
) => {
const _state = structuredClone(state);

switch (action.type) {
case 'update_roles': {
action.payload.forEach((role) => {
_state.roles.set(role.name, role);
});

return _state;
}
case 'remove_roles': {
action.payload.forEach((role) => {
_state.roles.delete(role.name);
});

return _state;
}
default: {
return _state;
}
}
};
Loading

0 comments on commit ccd34f3

Please sign in to comment.