feat(ui): compute user's effective permissions with roles
parent
7c1f492cfe
commit
14140740fb
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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},
|
||||
],
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue