Partial forward-port of dacf0c2a6c. (#113672)

- Add Authorization type to ES UI shared.
- Add convertPrivilegesToArray, patch to also accept privileges that might contain dots in its name, and add tests.

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
CJ Cenizal 2021-10-04 12:11:05 -07:00 committed by GitHub
parent abffa79ba2
commit 257e33a50e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 54 additions and 15 deletions

View file

@ -13,7 +13,7 @@ import { useRequest } from '../../../public/request';
import { Privileges, Error as CustomError } from '../types';
interface Authorization {
export interface Authorization {
isLoading: boolean;
apiError: CustomError | null;
privileges: Privileges;

View file

@ -10,6 +10,7 @@ export {
AuthorizationProvider,
AuthorizationContext,
useAuthorizationContext,
Authorization,
} from './authorization_provider';
export { WithPrivileges } from './with_privileges';

View file

@ -0,0 +1,28 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { convertPrivilegesToArray } from './with_privileges';
describe('convertPrivilegesToArray', () => {
test('extracts section and privilege', () => {
expect(convertPrivilegesToArray('index.index_name')).toEqual([['index', 'index_name']]);
expect(convertPrivilegesToArray(['index.index_name', 'cluster.management'])).toEqual([
['index', 'index_name'],
['cluster', 'management'],
]);
expect(convertPrivilegesToArray('index.index_name.with-many.dots')).toEqual([
['index', 'index_name.with-many.dots'],
]);
});
test('throws when it cannot extract section and privilege', () => {
expect(() => {
convertPrivilegesToArray('bad_privilege_string');
}).toThrow('Required privilege must have the format "section.privilege"');
});
});

View file

@ -10,13 +10,14 @@ import { MissingPrivileges } from '../types';
import { useAuthorizationContext } from './authorization_provider';
type Privileges = string | string[];
interface Props {
/**
* Each required privilege must have the format "section.privilege".
* To indicate that *all* privileges from a section are required, we can use the asterix
* e.g. "index.*"
*/
privileges: string | string[];
privileges: Privileges;
children: (childrenProps: {
isLoading: boolean;
hasPrivileges: boolean;
@ -26,24 +27,30 @@ interface Props {
type Privilege = [string, string];
const toArray = (value: string | string[]): string[] =>
const toArray = (value: Privileges): string[] =>
Array.isArray(value) ? (value as string[]) : ([value] as string[]);
export const convertPrivilegesToArray = (privileges: Privileges): Privilege[] => {
return toArray(privileges).map((p) => {
// Since an privilege can contain a dot in its name:
// * `section` needs to be extracted from the beginning of the string until the first dot
// * `privilege` should be everything after the dot
const indexOfFirstPeriod = p.indexOf('.');
if (indexOfFirstPeriod === -1) {
throw new Error('Required privilege must have the format "section.privilege"');
}
return [p.slice(0, indexOfFirstPeriod), p.slice(indexOfFirstPeriod + 1)];
});
};
export const WithPrivileges = ({ privileges: requiredPrivileges, children }: Props) => {
const { isLoading, privileges } = useAuthorizationContext();
const privilegesToArray: Privilege[] = toArray(requiredPrivileges).map((p) => {
const [section, privilege] = p.split('.');
if (!privilege) {
// Oh! we forgot to use the dot "." notation.
throw new Error('Required privilege must have the format "section.privilege"');
}
return [section, privilege];
});
const privilegesArray = convertPrivilegesToArray(requiredPrivileges);
const hasPrivileges = isLoading
? false
: privilegesToArray.every((privilege) => {
: privilegesArray.every((privilege) => {
const [section, requiredPrivilege] = privilege;
if (!privileges.missingPrivileges[section]) {
// if the section does not exist in our missingPriviledges, everything is OK
@ -61,7 +68,7 @@ export const WithPrivileges = ({ privileges: requiredPrivileges, children }: Pro
return !privileges.missingPrivileges[section]!.includes(requiredPrivilege);
});
const privilegesMissing = privilegesToArray.reduce((acc, [section, privilege]) => {
const privilegesMissing = privilegesArray.reduce((acc, [section, privilege]) => {
if (privilege === '*') {
acc[section] = privileges.missingPrivileges[section] || [];
} else if (

View file

@ -14,6 +14,7 @@ export {
SectionError,
PageError,
useAuthorizationContext,
Authorization,
} from './components';
export { Privileges, MissingPrivileges, Error } from './types';

View file

@ -6,4 +6,4 @@
* Side Public License, v 1.
*/
export { Privileges, MissingPrivileges } from '../__packages_do_not_import__/authorization';
export { Privileges, MissingPrivileges } from '../__packages_do_not_import__/authorization/types';

View file

@ -17,4 +17,5 @@ export {
PageError,
useAuthorizationContext,
WithPrivileges,
Authorization,
} from '../../__packages_do_not_import__/authorization';

View file

@ -45,6 +45,7 @@ export {
PageError,
Error,
useAuthorizationContext,
Authorization,
} from './authorization';
export { Forms, ace, GlobalFlyout, XJson };