feat(ui): compute user's effective permissions with roles

pull/5927/head
Pavel Zavora 2022-06-07 15:01:49 +02:00
parent 7c1f492cfe
commit 14140740fb
2 changed files with 292 additions and 18 deletions

View File

@ -5,15 +5,19 @@ type EntitiesDBPermissions = Array<Array<Record<string, boolean>>>
/** record with database names and their permissions */
type DBPermRecords = Record<string, Record<string, boolean>>
type GetDBPermRecords<E extends User | UserRole> = (e: E) => DBPermRecords
const getEmptyDBPermRecords: GetDBPermRecords<any> = () => ({})
/**
* ComputeDBPermRecords computes EntitiesDBPermissions for
* a suplied user/role and database names.
*/
export function computeDBPermRecords(
u: User | UserRole,
dbNames: string[]
export function computeDBPermRecords<E extends User | UserRole>(
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<E extends User | UserRole>(
entities: E[],
dbNames: string[],
getInitialDBRecords: GetDBPermRecords<E> = 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<string, [UserRole, DBPermRecords | undefined]>)
const getInitialPermRecord: GetDBPermRecords<User> = (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)
}

View File

@ -1,11 +1,146 @@
import {computeEntitiesDBPermissions} from 'src/admin/containers/influxdb/util/computeEffectiveDBPermissions'
import {User} from 'src/types/influxAdmin'
const redundantUserProperties: Pick<User, 'roles' | 'links'> = {
import {
computeDBPermRecords,
computeEffectiveUserDBPermissions,
computeEntitiesDBPermissions,
mergeDBPermRecords,
} from 'src/admin/containers/influxdb/util/computeEffectiveDBPermissions'
import {User, UserRole} from 'src/types/influxAdmin'
const emptyUserProperties: Pick<User, 'roles' | 'links'> = {
roles: [],
links: {self: ''},
}
const emptyRoleProperties: Pick<UserRole, 'users' | 'links'> = {
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},
],
])
})
})
})