diff --git a/ui/src/admin/containers/influxdb/util/computeEffectiveDBPermissions.ts b/ui/src/admin/containers/influxdb/util/computeEffectiveDBPermissions.ts index 08d313be4..a92b91610 100644 --- a/ui/src/admin/containers/influxdb/util/computeEffectiveDBPermissions.ts +++ b/ui/src/admin/containers/influxdb/util/computeEffectiveDBPermissions.ts @@ -5,15 +5,19 @@ type EntitiesDBPermissions = Array>> /** record with database names and their permissions */ type DBPermRecords = Record> +type GetDBPermRecords = (e: E) => DBPermRecords +const getEmptyDBPermRecords: GetDBPermRecords = () => ({}) + /** * ComputeDBPermRecords computes EntitiesDBPermissions for * a suplied user/role and database names. */ -export function computeDBPermRecords( - u: User | UserRole, - dbNames: string[] +export function computeDBPermRecords( + e: E, + dbNames: string[], + initialDBPermRecords: DBPermRecords = {} ): DBPermRecords { - return u.permissions.reduce((acc: DBPermRecords, perm: UserPermission) => { + return e.permissions.reduce((acc: DBPermRecords, perm: UserPermission) => { if (perm.scope === 'all') { const allowed = perm.allowed.includes('ALL') ? {READ: true, WRITE: true} @@ -32,7 +36,7 @@ export function computeDBPermRecords( ) } return acc - }, {} as DBPermRecords) + }, initialDBPermRecords) } /** @@ -52,12 +56,48 @@ export function mergeDBPermRecords(...records: DBPermRecords[]): DBPermRecords { * ComputeEntitiesDBPermissions computes EntitiesDBPermissions for * suplied users/roles and database names. */ -export function computeEntitiesDBPermissions( - entities: User[] | UserRole[], - dbNames: string[] +export function computeEntitiesDBPermissions( + entities: E[], + dbNames: string[], + getInitialDBRecords: GetDBPermRecords = getEmptyDBPermRecords ): EntitiesDBPermissions { - return entities.map((u: User | UserRole) => { - const dbPermRecords = computeDBPermRecords(u, dbNames) + return entities.map((e: E) => { + const dbPermRecords = computeDBPermRecords( + e, + dbNames, + getInitialDBRecords(e) + ) return dbNames.map(dbName => dbPermRecords[dbName] || {}) }) } + +/** + * ComputeEffectiveUserDBPermissions computes effective DB permissions for all supplied + * users from their permissions and their roles. + */ +export function computeEffectiveUserDBPermissions( + users: User[], + roles: UserRole[], + dbNames: string[] +): EntitiesDBPermissions { + const rolesDBPermRecords = roles.reduce((acc, role) => { + acc[role.name] = [role, undefined] + return acc + }, {} as Record) + const getInitialPermRecord: GetDBPermRecords = (u: User) => { + if (u.roles?.length) { + return u.roles.reduce((acc, {name: roleName}) => { + const pair = rolesDBPermRecords[roleName] + let rolePerms = pair[1] + if (!rolePerms) { + rolePerms = computeDBPermRecords(pair[0], dbNames) + pair[1] = rolePerms + } + return mergeDBPermRecords(acc, rolePerms) + }, {} as DBPermRecords) + } + return {} + } + + return computeEntitiesDBPermissions(users, dbNames, getInitialPermRecord) +} diff --git a/ui/test/admin/containers/influxdb/util/computeEffectiveDBPermissions.test.ts b/ui/test/admin/containers/influxdb/util/computeEffectiveDBPermissions.test.ts index 1907fea93..1a212e84d 100644 --- a/ui/test/admin/containers/influxdb/util/computeEffectiveDBPermissions.test.ts +++ b/ui/test/admin/containers/influxdb/util/computeEffectiveDBPermissions.test.ts @@ -1,11 +1,146 @@ -import {computeEntitiesDBPermissions} from 'src/admin/containers/influxdb/util/computeEffectiveDBPermissions' -import {User} from 'src/types/influxAdmin' -const redundantUserProperties: Pick = { +import { + computeDBPermRecords, + computeEffectiveUserDBPermissions, + computeEntitiesDBPermissions, + mergeDBPermRecords, +} from 'src/admin/containers/influxdb/util/computeEffectiveDBPermissions' +import {User, UserRole} from 'src/types/influxAdmin' +const emptyUserProperties: Pick = { roles: [], links: {self: ''}, } +const emptyRoleProperties: Pick = { + users: [], + links: {self: ''}, +} describe('admin/containers/influxdb/util/computeEffectiveDBPermissions', () => { + describe('computeDBPermRecords', () => { + const subject = computeDBPermRecords + it('creates value for no databases', () => { + expect( + subject({name: 'a', permissions: [], ...emptyUserProperties}, []) + ).toEqual({}) + expect( + subject({name: 'a', permissions: [], ...emptyUserProperties}, [], { + db1: {}, + }) + ).toEqual({db1: {}}) + }) + it('computes db-specific permissions', () => { + expect( + subject( + { + name: 'a', + permissions: [ + {scope: 'database', name: 'db1', allowed: ['A']}, + {scope: 'database', name: 'db3', allowed: ['B', 'C']}, + ], + ...emptyUserProperties, + }, + ['db1', 'db2', 'db3'] + ) + ).toEqual({db1: {A: true}, db3: {B: true, C: true}}) + }) + it('maps all-scoped ALL permission to READ, WRITE', () => { + expect( + subject( + { + name: 'a', + permissions: [{scope: 'all', allowed: ['ALL']}], + ...emptyUserProperties, + }, + ['db1', 'db2'] + ) + ).toEqual({ + db1: {READ: true, WRITE: true}, + db2: {READ: true, WRITE: true}, + }) + }) + it('inherits all permissions', () => { + expect( + subject( + { + name: 'a', + permissions: [ + {scope: 'all', allowed: ['Read']}, + {scope: 'database', name: 'db1', allowed: ['Write']}, + {scope: 'database', name: 'db2', allowed: ['Other']}, + ], + ...emptyUserProperties, + }, + ['db1', 'db2'] + ) + ).toEqual({ + db1: {Read: true, Write: true}, + db2: {Read: true, Other: true}, + }) + }) + it('inherits independently on order', () => { + expect( + subject( + { + name: 'a', + permissions: [ + {scope: 'database', name: 'db2', allowed: ['Other']}, + {scope: 'database', name: 'db1', allowed: ['Write']}, + {scope: 'all', allowed: ['Read']}, + ], + ...emptyUserProperties, + }, + ['db1', 'db2', 'db3'] + ) + ).toEqual({ + db1: {Read: true, Write: true}, + db2: {Read: true, Other: true}, + db3: {Read: true}, + }) + }) + it('uses custom initial DB perms', () => { + expect( + subject( + { + name: 'a', + permissions: [ + {scope: 'database', name: 'db2', allowed: ['Other']}, + {scope: 'database', name: 'db1', allowed: ['Write']}, + {scope: 'all', allowed: ['Read']}, + ], + ...emptyUserProperties, + }, + ['db1', 'db2', 'db3'], + {a: {Other: true}, db3: {Write: true}} + ) + ).toEqual({ + a: {Other: true}, + db1: {Read: true, Write: true}, + db2: {Read: true, Other: true}, + db3: {Read: true, Write: true}, + }) + }) + }) + describe('mergeDBPermRecords', () => { + const subject = mergeDBPermRecords + it('can merge no records', () => { + expect(subject()).toEqual({}) + }) + it('can merge empty records', () => { + expect(subject({}, {}, {})).toEqual({}) + }) + it('can merge perm non-empty records', () => { + expect( + subject( + {db1: {R: true}}, + {db2: {W: true}}, + {db1: {W: true}, db2: {R: true}, db3: {O: true, C: true}} + ) + ).toEqual({ + db1: {R: true, W: true}, + db2: {R: true, W: true}, + db3: {O: true, C: true}, + }) + }) + }) describe('computeEntitiesDBPermissions', () => { const subject = computeEntitiesDBPermissions it('creates values for empty users', () => { @@ -13,7 +148,7 @@ describe('admin/containers/influxdb/util/computeEffectiveDBPermissions', () => { }) it('creates values for no databases', () => { expect( - subject([{name: 'a', permissions: [], ...redundantUserProperties}], []) + subject([{name: 'a', permissions: [], ...emptyUserProperties}], []) ).toEqual([[]]) }) it('computes db-specific permissions', () => { @@ -26,7 +161,7 @@ describe('admin/containers/influxdb/util/computeEffectiveDBPermissions', () => { {scope: 'database', name: 'db1', allowed: ['A']}, {scope: 'database', name: 'db3', allowed: ['B', 'C']}, ], - ...redundantUserProperties, + ...emptyUserProperties, }, ], ['db1', 'db2', 'db3'] @@ -40,7 +175,7 @@ describe('admin/containers/influxdb/util/computeEffectiveDBPermissions', () => { { name: 'a', permissions: [{scope: 'all', allowed: ['ALL']}], - ...redundantUserProperties, + ...emptyUserProperties, }, ], ['db1', 'db2'] @@ -63,7 +198,7 @@ describe('admin/containers/influxdb/util/computeEffectiveDBPermissions', () => { {scope: 'database', name: 'db1', allowed: ['Write']}, {scope: 'database', name: 'db2', allowed: ['Other']}, ], - ...redundantUserProperties, + ...emptyUserProperties, }, ], ['db1', 'db2'] @@ -86,7 +221,7 @@ describe('admin/containers/influxdb/util/computeEffectiveDBPermissions', () => { {scope: 'database', name: 'db1', allowed: ['Write']}, {scope: 'all', allowed: ['Read']}, ], - ...redundantUserProperties, + ...emptyUserProperties, }, ], ['db1', 'db2', 'db3'] @@ -96,4 +231,103 @@ describe('admin/containers/influxdb/util/computeEffectiveDBPermissions', () => { ]) }) }) + describe('computeEffectiveUserDBPermissions', () => { + const subject = computeEffectiveUserDBPermissions + it('creates values for empty users', () => { + expect(subject([], [], ['whateverdb'])).toEqual([]) + }) + it('creates values for no databases', () => { + expect( + subject([{...emptyUserProperties, name: 'a', permissions: []}], [], []) + ).toEqual([[]]) + }) + it('computes effective permissions', () => { + expect( + subject( + [ + { + ...emptyUserProperties, + name: 'a', + permissions: [ + {scope: 'database', name: 'db1', allowed: ['A']}, + {scope: 'database', name: 'db3', allowed: ['B', 'C']}, + ], + roles: [ + { + ...emptyRoleProperties, + name: 'ra', + permissions: [], + }, + { + ...emptyRoleProperties, + name: 'rb', + permissions: [], + }, + ], + }, + ], + [ + { + ...emptyRoleProperties, + name: 'ra', + permissions: [{scope: 'database', name: 'db1', allowed: ['B']}], + }, + { + ...emptyRoleProperties, + name: 'rb', + permissions: [{scope: 'database', name: 'db2', allowed: ['B']}], + }, + ], + ['db1', 'db2', 'db3'] + ) + ).toEqual([[{A: true, B: true}, {B: true}, {B: true, C: true}]]) + }) + it('inherits all permissions from role', () => { + expect( + subject( + [ + { + ...emptyUserProperties, + name: 'a', + permissions: [ + {scope: 'database', name: 'db1', allowed: ['W']}, + {scope: 'all', allowed: ['R']}, + {scope: 'database', name: 'db2', allowed: ['O']}, + ], + roles: [ + { + ...emptyRoleProperties, + name: 'ra', + permissions: [], + }, + { + ...emptyRoleProperties, + name: 'rb', + permissions: [], + }, + ], + }, + ], + [ + { + ...emptyRoleProperties, + name: 'ra', + permissions: [{scope: 'database', name: 'db1', allowed: ['B']}], + }, + { + ...emptyRoleProperties, + name: 'rb', + permissions: [{scope: 'all', allowed: ['C']}], + }, + ], + ['db1', 'db2'] + ) + ).toEqual([ + [ + {R: true, W: true, B: true, C: true}, + {R: true, O: true, C: true}, + ], + ]) + }) + }) })