mirror of https://github.com/Squidex/squidex.git
36 changed files with 555 additions and 149 deletions
@ -0,0 +1,106 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Permission, permissionsAllow } from './permission'; |
|||
|
|||
describe('Permission', () => { |
|||
it('Should_return_true_if_given_and_requested_permission_have_wildcards', () => { |
|||
const g = new Permission('app.*'); |
|||
const r = new Permission('app.*'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('Should_return_true_if_given_permission_equals_requested_permission', () => { |
|||
const g = new Permission('app.contents'); |
|||
const r = new Permission('app.contents'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('Should_return_true_if_given_permission_is_parent_of_requested_permission', () => { |
|||
const g = new Permission('app'); |
|||
const r = new Permission('app.contents'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('Should_return_true_if_given_permission_is_alternative_of_requested_permission', () => { |
|||
const g = new Permission('app.contents|schemas'); |
|||
const r = new Permission('app.contents'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('Should_return_true_if_given_permission_equals_alternative_requested_permission', () => { |
|||
const g = new Permission('app.contents'); |
|||
const r = new Permission('app.contents|schemas'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('Should_return_true_if_given_permission_has_wildcard_for_requested_permission', () => { |
|||
const g = new Permission('app.*'); |
|||
const r = new Permission('app.contents'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('Should_return_false_if_given_permission_not_equals_requested_permission', () => { |
|||
const g = new Permission('app.contents'); |
|||
const r = new Permission('app.assets'); |
|||
|
|||
expect(g.allows(r)).toBeFalsy(); |
|||
}); |
|||
|
|||
it('Should_return_false_if_given_permission_is_child_of_requested_permission', () => { |
|||
const g = new Permission('app.contents'); |
|||
const r = new Permission('app'); |
|||
|
|||
expect(g.allows(r)).toBeFalsy(); |
|||
}); |
|||
|
|||
it('Should_return_false_if_given_permission_has_no_wildcard_but_requested_has', () => { |
|||
const g = new Permission('app.contents'); |
|||
const r = new Permission('app.*'); |
|||
|
|||
expect(g.allows(r)).toBeFalsy(); |
|||
}); |
|||
|
|||
it('Should_return_false_if_given_requested_permission_is_null', () => { |
|||
const g = new Permission('app.contents'); |
|||
|
|||
expect(g.allows(null!)).toBeFalsy(); |
|||
}); |
|||
|
|||
it('Should_return_true_if_any_permission_gives_permission_to_request', () => { |
|||
const sut = [ |
|||
new Permission('app.contents'), |
|||
new Permission('app.assets') |
|||
]; |
|||
|
|||
expect(permissionsAllow(sut, new Permission('app.contents'))).toBeTruthy(); |
|||
}); |
|||
|
|||
it('Should_return_false_if_none_permission_gives_permission_to_request', () => { |
|||
const sut = [ |
|||
new Permission('app.contents'), |
|||
new Permission('app.assets') |
|||
]; |
|||
|
|||
expect(permissionsAllow(sut, new Permission('app.schemas'))).toBeFalsy(); |
|||
}); |
|||
|
|||
it('Should_return_false_if_permission_to_request_is_null', () => { |
|||
const sut = [ |
|||
new Permission('app.contents'), |
|||
new Permission('app.assets') |
|||
]; |
|||
|
|||
expect(permissionsAllow(sut, null!)).toBeFalsy(); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,85 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Types } from './types'; |
|||
|
|||
export class Permission { |
|||
private readonly parts: ({ [key: string]: true } | null)[]; |
|||
|
|||
constructor( |
|||
public readonly id: string |
|||
) { |
|||
this.parts = id.split('.').map(x => { |
|||
if (x === '*') { |
|||
return null; |
|||
} else { |
|||
const result: { [key: string]: true } = {}; |
|||
|
|||
for (let p of x.split('|')) { |
|||
result[p] = true; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public allows(permission?: Permission | string) { |
|||
if (!permission) { |
|||
return false; |
|||
} |
|||
|
|||
if (Types.isString(permission)) { |
|||
permission = new Permission(permission); |
|||
} |
|||
|
|||
if (this.parts.length > permission.parts.length) { |
|||
return false; |
|||
} |
|||
|
|||
for (let i = 0; i < this.parts.length; i++) { |
|||
let lhs = this.parts[i]; |
|||
let rhs = permission.parts[i]; |
|||
|
|||
if (lhs !== null && (rhs === null || !Permission.any(lhs, rhs))) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private static any(lhs: { [key: string]: true }, rhs: { [key: string]: true }) { |
|||
for (let key in lhs) { |
|||
if (rhs[key]) { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
for (let key in rhs) { |
|||
if (lhs[key]) { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
|
|||
export function permissionsAllow(permissions: Permission[], other: Permission) { |
|||
if (!other) { |
|||
return false; |
|||
} |
|||
|
|||
for (let permission of permissions) { |
|||
if (permission.allows(other)) { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Directive, Input, OnChanges, TemplateRef, ViewContainerRef } from '@angular/core'; |
|||
|
|||
import { |
|||
AppDto, |
|||
AppsState, |
|||
AuthService, |
|||
Permission, |
|||
permissionsAllow, |
|||
SchemaDto, |
|||
SchemasState |
|||
} from '@app/shared/internal'; |
|||
|
|||
@Directive({ |
|||
selector: '[sqxPermission]' |
|||
}) |
|||
export class PermissionDirective implements OnChanges { |
|||
private viewCreated = false; |
|||
|
|||
@Input('sqxPermissionApp') |
|||
public app?: AppDto; |
|||
|
|||
@Input('sqxPermissionSchema') |
|||
public schema?: SchemaDto; |
|||
|
|||
@Input('sqxPermission') |
|||
public permissions: string; |
|||
|
|||
constructor( |
|||
private readonly authService: AuthService, |
|||
private readonly appsState: AppsState, |
|||
private readonly schemasState: SchemasState, |
|||
private readonly templateRef: TemplateRef<any>, |
|||
private readonly viewContainer: ViewContainerRef |
|||
) { |
|||
} |
|||
|
|||
public ngOnChanges() { |
|||
let show = false; |
|||
|
|||
if (this.permissions) { |
|||
for (let id of this.permissions.split(';')) { |
|||
const app = this.app || this.appsState.snapshot.selectedApp; |
|||
|
|||
if (app) { |
|||
id = id.replace('{app}', app.name); |
|||
} |
|||
|
|||
const schema = this.schema || this.schemasState.snapshot.selectedSchema; |
|||
|
|||
if (schema) { |
|||
id = id.replace('{name}', schema.name); |
|||
} |
|||
|
|||
const permission = new Permission(id); |
|||
|
|||
if (app && permissionsAllow(app.permissions, permission)) { |
|||
show = true; |
|||
} |
|||
|
|||
if (!show) { |
|||
show = permissionsAllow(this.authService.user!.permissions, permission); |
|||
} |
|||
|
|||
if (show) { |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (show && !this.viewCreated) { |
|||
this.viewContainer.createEmbeddedView(this.templateRef); |
|||
this.viewCreated = true; |
|||
} else if (show && this.viewCreated) { |
|||
this.viewContainer.clear(); |
|||
this.viewCreated = false; |
|||
} |
|||
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue