Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GAP - Role API Structure #26740

Merged
merged 18 commits into from
Dec 6, 2018
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`#deserializePrivilegeAssignedAtSpace throws Error if not prefixed with space_ 1`] = `"Unable to deserialize all, should have started with space_ or feature_"`;
exports[`#deserializeFeaturePrivilege throws error when deserializing feature_.privilege-1 1`] = `"Feature privilege 'feature_.privilege-1' didn't match pattern"`;

exports[`#deserializePrivilegeAssignedAtSpace throws Error if prefixed with space_ but not a reserved privilege 1`] = `"Unrecognized privilege assigned at space"`;
exports[`#deserializeFeaturePrivilege throws error when deserializing feature_foo. 1`] = `"Feature privilege 'feature_foo.' didn't match pattern"`;

exports[`#deserializePrivilegeAssignedGlobally throws Error if not prefixed with feature_ and isn't a reserved privilege 1`] = `"Unrecognized privilege assigned globally"`;
exports[`#deserializeFeaturePrivilege throws error when deserializing feature_foo_privilege-1 1`] = `"Feature privilege 'feature_foo_privilege-1' didn't match pattern"`;

exports[`#serializeGlobalPrivilege throws Error if unrecognized privilege used 1`] = `"Unrecognized global reserved privilege"`;
exports[`#deserializeFeaturePrivilege throws error when deserializing feature-foo.privilege-1 1`] = `"Feature privilege 'feature-foo.privilege-1' didn't match pattern"`;

exports[`#serializeSpaceReservedPrivilege throws Error if unrecognized privilege used 1`] = `"Unrecognized space reserved privilege"`;
exports[`#deserializeFeaturePrivilege throws error when deserializing foo_feature_foo.privilege-1 1`] = `"Feature privilege 'foo_feature_foo.privilege-1' didn't match pattern"`;

exports[`#deserializeGlobalMinimumPrivilege throws Error if isn't a minimum privilege 1`] = `"Unrecognized global minimum privilege"`;

exports[`#deserializeSpaceMinimumPrivilege throws Error if prefixed with space_ but not a reserved privilege 1`] = `"Unrecognized space minimum privilege"`;

exports[`#deserializeSpaceMinimumPrivilege throws Error if provided 'all' 1`] = `"Unrecognized space minimum privilege"`;

exports[`#serializeGlobalMinimumPrivilege throws Error if unrecognized privilege used 1`] = `"Unrecognized global minimum privilege"`;

exports[`#serializeSpaceMinimumPrivilege throws Error if unrecognized privilege used 1`] = `"Unrecognized space minimum privilege"`;
Original file line number Diff line number Diff line change
Expand Up @@ -6,131 +6,155 @@

import { PrivilegeSerializer } from './privilege_serializer';

describe('#serializeGlobalPrivilege', () => {
describe(`#isGlobalMinimumPrivilege`, () => {
['all', 'read'].forEach(validValue => {
test(`returns true for '${validValue}'`, () => {
expect(PrivilegeSerializer.isGlobalMinimumPrivilege(validValue)).toBe(true);
});
});

['space_all', 'space_read', 'foo', 'bar', 'feature_foo', 'feature_foo.privilege1'].forEach(
validValue => {
kobelb marked this conversation as resolved.
Show resolved Hide resolved
test(`returns true for '${validValue}'`, () => {
kobelb marked this conversation as resolved.
Show resolved Hide resolved
expect(PrivilegeSerializer.isGlobalMinimumPrivilege(validValue)).toBe(false);
kobelb marked this conversation as resolved.
Show resolved Hide resolved
});
}
);
});

describe(`#isSpaceMinimumPrivilege`, () => {
['space_all', 'space_read'].forEach(validValue => {
test(`returns true for '${validValue}'`, () => {
expect(PrivilegeSerializer.isSpaceMinimumPrivilege(validValue)).toBe(true);
});
});

['all', 'read', 'foo', 'bar', 'feature_foo', 'feature_foo.privilege1'].forEach(validValue => {
test(`returns true for '${validValue}'`, () => {
kobelb marked this conversation as resolved.
Show resolved Hide resolved
expect(PrivilegeSerializer.isSpaceMinimumPrivilege(validValue)).toBe(false);
});
});
});

describe('#serializeGlobalMinimumPrivilege', () => {
test('throws Error if unrecognized privilege used', () => {
expect(() =>
PrivilegeSerializer.serializeGlobalReservedPrivilege('foo')
PrivilegeSerializer.serializeGlobalMinimumPrivilege('foo')
).toThrowErrorMatchingSnapshot();
});

test('returns all unmodified', () => {
const allResult = PrivilegeSerializer.serializeGlobalReservedPrivilege('all');
const allResult = PrivilegeSerializer.serializeGlobalMinimumPrivilege('all');
expect(allResult).toBe('all');
});

test('returns read unmodified', () => {
const readResult = PrivilegeSerializer.serializeGlobalReservedPrivilege('read');
const readResult = PrivilegeSerializer.serializeGlobalMinimumPrivilege('read');
expect(readResult).toBe('read');
});
});

describe('#serializeSpaceReservedPrivilege', () => {
describe('#serializeSpaceMinimumPrivilege', () => {
test('throws Error if unrecognized privilege used', () => {
expect(() =>
PrivilegeSerializer.serializeSpaceReservedPrivilege('foo')
PrivilegeSerializer.serializeSpaceMinimumPrivilege('foo')
).toThrowErrorMatchingSnapshot();
});

test('returns all prefixed with space_', () => {
const allResult = PrivilegeSerializer.serializeSpaceReservedPrivilege('all');
const allResult = PrivilegeSerializer.serializeSpaceMinimumPrivilege('all');
expect(allResult).toBe('space_all');
});

test('returns read prefixed with space_', () => {
const readResult = PrivilegeSerializer.serializeSpaceReservedPrivilege('read');
const readResult = PrivilegeSerializer.serializeSpaceMinimumPrivilege('read');
expect(readResult).toBe('space_read');
});
});

describe('#serializeFeaturePrivilege', () => {
test('returns `feature_${featureName}_${privilegeName}`', () => {
test('returns `feature_${featureName}.${privilegeName}`', () => {
const result = PrivilegeSerializer.serializeFeaturePrivilege('foo', 'bar');
expect(result).toBe('feature_foo_bar');
});
});

describe('#serializePrivilegeAssignedGlobally', () => {
test(`returns 'all' when 'all' is provided`, () => {
const result = PrivilegeSerializer.serializePrivilegeAssignedGlobally('all');
expect(result).toBe('all');
});

test(`returns 'read' when 'read' is provided`, () => {
const result = PrivilegeSerializer.serializePrivilegeAssignedGlobally('read');
expect(result).toBe('read');
});

test('returns `feature_${privilege}` otherwise', () => {
const result = PrivilegeSerializer.serializePrivilegeAssignedGlobally('foo');
expect(result).toBe('feature_foo');
expect(result).toBe('feature_foo.bar');
});
});

describe('#serializePrivilegeAssignedAtSpace', () => {
test(`returns 'space_all' when 'all' is provided`, () => {
const result = PrivilegeSerializer.serializePrivilegeAssignedAtSpace('all');
expect(result).toBe('space_all');
});

test(`returns 'space_read' when 'read' is provided`, () => {
const result = PrivilegeSerializer.serializePrivilegeAssignedAtSpace('read');
expect(result).toBe('space_read');
});

test('returns `feature_${privilege}` otherwise', () => {
const result = PrivilegeSerializer.serializePrivilegeAssignedAtSpace('foo');
expect(result).toBe('feature_foo');
describe('#deserializeFeaturePrivilege', () => {
[
{
privilege: 'feature_foo.privilege-1',
expectedResult: {
featureId: 'foo',
privilege: 'privilege-1',
},
},
{
privilege: 'feature_foo_bar.foo_privilege-1',
expectedResult: {
featureId: 'foo_bar',
privilege: 'foo_privilege-1',
},
},
].forEach(({ privilege, expectedResult }) => {
test(`deserializes '${privilege}' to ${JSON.stringify(expectedResult)}`, () => {
const result = PrivilegeSerializer.deserializeFeaturePrivilege(privilege);
expect(result).toEqual(expectedResult);
});
});

[
'feature-foo.privilege-1', // doesn't start with feature_
'foo_feature_foo.privilege-1', // also doesn't start with feature_
'feature_foo_privilege-1', // no '.'
'feature_foo.', // has a '.' but nothing after it
'feature_.privilege-1', // nothing before the '.'
].forEach(privilege => {
test(`throws error when deserializing ${privilege}`, () => {
expect(() =>
PrivilegeSerializer.deserializeFeaturePrivilege(privilege)
).toThrowErrorMatchingSnapshot();
});
});
});

describe('#deserializePrivilegeAssignedGlobally', () => {
test(`if prefixed with 'feature_' removes the prefix`, () => {
const result = PrivilegeSerializer.deserializePrivilegeAssignedGlobally('feature_foo');
expect(result).toBe('foo');
});

test(`throws Error if not prefixed with feature_ and isn't a reserved privilege`, () => {
describe('#deserializeGlobalMinimumPrivilege', () => {
test(`throws Error if isn't a minimum privilege`, () => {
expect(() =>
PrivilegeSerializer.deserializePrivilegeAssignedGlobally('foo')
PrivilegeSerializer.deserializeGlobalMinimumPrivilege('foo')
).toThrowErrorMatchingSnapshot();
});

test(`returns 'all' unprefixed if provided 'all'`, () => {
const result = PrivilegeSerializer.deserializePrivilegeAssignedGlobally('all');
const result = PrivilegeSerializer.deserializeGlobalMinimumPrivilege('all');
expect(result).toBe('all');
});

test(`returns 'read' unprefixed if provided 'read'`, () => {
const result = PrivilegeSerializer.deserializePrivilegeAssignedGlobally('read');
const result = PrivilegeSerializer.deserializeGlobalMinimumPrivilege('read');
expect(result).toBe('read');
});
});

describe('#deserializePrivilegeAssignedAtSpace', () => {
test(`if prefixed with 'feature_' removes the prefix`, () => {
const result = PrivilegeSerializer.deserializePrivilegeAssignedAtSpace('feature_foo');
expect(result).toBe('foo');
});

test(`throws Error if not prefixed with space_`, () => {
describe('#deserializeSpaceMinimumPrivilege', () => {
test(`throws Error if provided 'all'`, () => {
expect(() =>
PrivilegeSerializer.deserializePrivilegeAssignedAtSpace('all')
PrivilegeSerializer.deserializeSpaceMinimumPrivilege('all')
).toThrowErrorMatchingSnapshot();
});

test(`throws Error if prefixed with space_ but not a reserved privilege`, () => {
expect(() =>
PrivilegeSerializer.deserializePrivilegeAssignedAtSpace('space_foo')
PrivilegeSerializer.deserializeSpaceMinimumPrivilege('space_foo')
).toThrowErrorMatchingSnapshot();
});

test(`returns 'all' unprefixed if provided 'space_all'`, () => {
const result = PrivilegeSerializer.deserializePrivilegeAssignedAtSpace('space_all');
const result = PrivilegeSerializer.deserializeSpaceMinimumPrivilege('space_all');
expect(result).toBe('all');
});

test(`returns 'read' unprefixed if provided 'space_read'`, () => {
const result = PrivilegeSerializer.deserializePrivilegeAssignedAtSpace('space_read');
const result = PrivilegeSerializer.deserializeSpaceMinimumPrivilege('space_read');
expect(result).toBe('read');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,73 +6,73 @@

const featurePrefix = 'feature_';
const spacePrefix = 'space_';
const reservedPrivilegeNames = ['all', 'read'];
const minimumPrivilegeNames = ['all', 'read'];
const spaceMinimumPrivilegeNames = minimumPrivilegeNames.map(
privilegeName => `${spacePrefix}${privilegeName}`
);
const deserializeFeaturePrivilegeRegexp = new RegExp(
`^${featurePrefix}([a-zA-Z0-9_-]+)\\.([a-zA-Z0-9_-]+)$`
);

interface FeaturePrivilege {
featureId: string;
privilege: string;
}

export class PrivilegeSerializer {
public static serializeGlobalReservedPrivilege(privilegeName: string) {
if (!reservedPrivilegeNames.includes(privilegeName)) {
throw new Error('Unrecognized global reserved privilege');
public static isGlobalMinimumPrivilege(privilegeName: string) {
return minimumPrivilegeNames.includes(privilegeName);
}

public static isSpaceMinimumPrivilege(privilegeName: string) {
return spaceMinimumPrivilegeNames.includes(privilegeName);
}

public static serializeGlobalMinimumPrivilege(privilegeName: string) {
if (!minimumPrivilegeNames.includes(privilegeName)) {
legrego marked this conversation as resolved.
Show resolved Hide resolved
throw new Error('Unrecognized global minimum privilege');
}

return privilegeName;
}

public static serializeSpaceReservedPrivilege(privilegeName: string) {
if (!reservedPrivilegeNames.includes(privilegeName)) {
throw new Error('Unrecognized space reserved privilege');
public static serializeSpaceMinimumPrivilege(privilegeName: string) {
if (!minimumPrivilegeNames.includes(privilegeName)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this read if (!spaceMinimumPrivilegeNames.includes(privilegeName))...?

In any case, same nit here: what do you think about using isSpaceMinimumPrivilege here, instead of duplicating the admittedly small amount of logic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unserialized, space and global privileges are both [ 'all', 'read' ], so we don't want to be using isSpaceMinimumPrivilege, but using minimumPrivilegeNames is confusing... lemme try and clean up the naming of these variables.

throw new Error('Unrecognized space minimum privilege');
}

return `${spacePrefix}${privilegeName}`;
}

public static serializeFeaturePrivilege(featureName: string, privilegeName: string) {
return `${featurePrefix}${featureName}_${privilegeName}`;
}

public static serializePrivilegeAssignedGlobally(privilege: string) {
if (reservedPrivilegeNames.includes(privilege)) {
return privilege;
}

return `${featurePrefix}${privilege}`;
return `${featurePrefix}${featureName}.${privilegeName}`;
}

public static serializePrivilegeAssignedAtSpace(privilege: string) {
if (reservedPrivilegeNames.includes(privilege)) {
return `${spacePrefix}${privilege}`;
public static deserializeFeaturePrivilege(privilege: string): FeaturePrivilege {
const match = privilege.match(deserializeFeaturePrivilegeRegexp);
if (!match) {
throw new Error(`Feature privilege '${privilege}' didn't match pattern`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we include a potential cause or fix as part of this message?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should only happen if the user is creating Kibana privileges directly using the ES APIs, or we messed something up.

}

return `${featurePrefix}${privilege}`;
return {
featureId: match[1],
privilege: match[2],
};
}

public static deserializePrivilegeAssignedGlobally(privilege: string) {
if (privilege.startsWith(featurePrefix)) {
return privilege.slice(featurePrefix.length);
}

if (reservedPrivilegeNames.includes(privilege)) {
public static deserializeGlobalMinimumPrivilege(privilege: string) {
if (PrivilegeSerializer.isGlobalMinimumPrivilege(privilege)) {
return privilege;
}

throw new Error('Unrecognized privilege assigned globally');
throw new Error('Unrecognized global minimum privilege');
}

public static deserializePrivilegeAssignedAtSpace(privilege: string) {
if (privilege.startsWith(featurePrefix)) {
return privilege.slice(featurePrefix.length);
}

if (!privilege.startsWith(spacePrefix)) {
throw new Error(
`Unable to deserialize ${privilege}, should have started with ${spacePrefix} or ${featurePrefix}`
);
}

const privilegeName = privilege.slice(spacePrefix.length);
if (reservedPrivilegeNames.includes(privilegeName)) {
return privilegeName;
public static deserializeSpaceMinimumPrivilege(privilege: string) {
if (!PrivilegeSerializer.isSpaceMinimumPrivilege(privilege)) {
throw new Error('Unrecognized space minimum privilege');
}

throw new Error('Unrecognized privilege assigned at space');
return privilege.slice(spacePrefix.length);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const serializePrivileges = (
[application]: {
...Object.entries(privilegeMap.global).reduce(
(acc, [privilegeName, privilegeActions]) => {
const name = PrivilegeSerializer.serializeGlobalReservedPrivilege(privilegeName);
const name = PrivilegeSerializer.serializeGlobalMinimumPrivilege(privilegeName);
acc[name] = {
application,
name: privilegeName,
Expand All @@ -43,7 +43,7 @@ export const serializePrivileges = (
),
...Object.entries(privilegeMap.space).reduce(
(acc, [privilegeName, privilegeActions]) => {
const name = PrivilegeSerializer.serializeSpaceReservedPrivilege(privilegeName);
const name = PrivilegeSerializer.serializeSpaceMinimumPrivilege(privilegeName);
acc[name] = {
application,
name,
Expand Down
Loading