-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
180 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './service'; | ||
export * from './transform'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './permission'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './role'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { GrantedRoleMap, grantedRoleMapReader, KnownGrantedRole } from './role'; | ||
|
||
const FIRST_ROLE = 'first'; | ||
const SECOND_ROLE = 'second'; | ||
|
||
type TestRoles = KnownGrantedRole | typeof FIRST_ROLE | typeof SECOND_ROLE; | ||
|
||
describe('grantedRoleMapReader()', () => { | ||
const rolesMap: GrantedRoleMap<TestRoles> = { | ||
read: true, | ||
first: true | ||
}; | ||
|
||
it('should create a reader for the input.', () => { | ||
const result = grantedRoleMapReader(rolesMap); | ||
expect(result).toBeDefined(); | ||
}); | ||
|
||
describe('reader', () => { | ||
const reader = grantedRoleMapReader(rolesMap); | ||
|
||
describe('hasRole', () => { | ||
it('should return true if the role is granted.', () => { | ||
expect(reader.hasRole('first')).toBe(true); | ||
}); | ||
|
||
it('should return false if the role not is granted.', () => { | ||
expect(reader.hasRole('second')).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('containsRoles', () => { | ||
describe('any', () => { | ||
it('should return true if the role is granted.', () => { | ||
expect(reader.containsRoles('any', 'first')).toBe(true); | ||
}); | ||
|
||
it('should return true if the roles are granted.', () => { | ||
expect(reader.containsRoles('any', ['read', 'first'])).toBe(true); | ||
}); | ||
|
||
it('should return true if any role is granted.', () => { | ||
expect(reader.containsRoles('any', ['read', 'second'])).toBe(true); | ||
}); | ||
|
||
it('should return false if none of the input roles are granted.', () => { | ||
expect(reader.containsRoles('any', 'second')).toBe(false); | ||
}); | ||
}); | ||
describe('all', () => { | ||
it('should return true if all roles are granted.', () => { | ||
expect(reader.containsRoles('all', ['read', 'first'])).toBe(true); | ||
}); | ||
|
||
it('should return false if only some of the input roles are granted.', () => { | ||
expect(reader.containsRoles('all', ['read', 'second'])).toBe(false); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { ArrayOrValue, asArray, Maybe, SetIncludesMode } from '@dereekb/util'; | ||
|
||
/** | ||
* A granted role for a model. | ||
*/ | ||
export type GrantedRole = string; | ||
|
||
export const GRANTED_READ_ROLE_KEY = 'read'; | ||
|
||
/** | ||
* Communicates that the current context has read access to a model. | ||
*/ | ||
export type GrantedReadRole = typeof GRANTED_READ_ROLE_KEY; | ||
|
||
export type KnownGrantedRole = GrantedReadRole; | ||
|
||
export const GRANTED_FULL_ACCESS_ROLE_KEY = '__FULL__'; | ||
|
||
/** | ||
* Communicates that the current context has full access to a model. | ||
*/ | ||
export type GrantedFullAccessGrantedRole = typeof GRANTED_FULL_ACCESS_ROLE_KEY; | ||
|
||
export const NO_ACCESS_ROLES_MAP_KEY = '__EMPTY__'; | ||
export type NoAccessGrantedRole = typeof NO_ACCESS_ROLES_MAP_KEY; | ||
|
||
export type NoAccessRolesMap = { | ||
[NO_ACCESS_ROLES_MAP_KEY]: true; | ||
}; | ||
|
||
export type FullAccessRolesMap = { | ||
[GRANTED_FULL_ACCESS_ROLE_KEY]: true; | ||
}; | ||
|
||
export type GrantedRoleMap<T extends GrantedRole = string> = NoAccessRolesMap | FullAccessRolesMap | GrantedRoleKeysMap<T>; | ||
|
||
export type GrantedRoleKeysMap<T extends GrantedRole = string> = { | ||
[key in T]?: Maybe<boolean>; | ||
}; | ||
|
||
export interface GrantedRoleMapReader<T extends GrantedRole = string> { | ||
/** | ||
* Returns true if no access has been given. | ||
*/ | ||
hasNoAccess(): boolean; | ||
|
||
/** | ||
* Returns true if the role is granted. | ||
*/ | ||
hasRole(role: T): boolean; | ||
|
||
/** | ||
* Returns true if the roles are granted. | ||
*/ | ||
hasRoles(setIncludes: SetIncludesMode, roles: ArrayOrValue<T>): boolean; | ||
|
||
/** | ||
* Returns true if the map explicitly contains the role. | ||
*/ | ||
containsRoles(setIncludes: SetIncludesMode, roles: ArrayOrValue<T>): boolean; | ||
} | ||
|
||
export function grantedRoleMapReader<T extends GrantedRole = string>(map: GrantedRoleMap<T>): GrantedRoleMapReader<T> { | ||
return new GrantedRoleMapReaderInstance(map); | ||
} | ||
|
||
export class GrantedRoleMapReaderInstance<T extends GrantedRole = string> implements GrantedRoleMapReader<T> { | ||
constructor(private readonly _map: GrantedRoleMap<T>) {} | ||
|
||
hasNoAccess(): boolean { | ||
return (this._map as NoAccessRolesMap)[NO_ACCESS_ROLES_MAP_KEY]; | ||
} | ||
|
||
hasRole(role: T): boolean { | ||
return this.hasRoles('any', role); | ||
} | ||
|
||
hasRoles(setIncludes: SetIncludesMode, inputRoles: ArrayOrValue<T>): boolean { | ||
if ((this._map as FullAccessRolesMap)[GRANTED_FULL_ACCESS_ROLE_KEY]) { | ||
return true; | ||
} else { | ||
return this.containsRoles(setIncludes, inputRoles); | ||
} | ||
} | ||
|
||
containsRoles(setIncludes: SetIncludesMode, inputRoles: ArrayOrValue<T>): boolean { | ||
const roles = asArray(inputRoles); | ||
|
||
if (setIncludes === 'any') { | ||
return this.containsAnyRole(roles); | ||
} else { | ||
return this.containsEachRole(roles); | ||
} | ||
} | ||
|
||
containsAnyRole(roles: GrantedRole[]): boolean { | ||
for (const role of roles) { | ||
if ((this._map as GrantedRoleKeysMap)[role]) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
containsEachRole(roles: GrantedRole[]): boolean { | ||
for (let role of roles) { | ||
if (!(this._map as GrantedRoleKeysMap)[role]) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters