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