mirror of https://github.com/Squidex/squidex.git
40 changed files with 369 additions and 610 deletions
@ -0,0 +1,39 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Areas.Api.Controllers.Backups; |
|||
using Squidex.Areas.Api.Controllers.EventConsumers; |
|||
using Squidex.Shared; |
|||
using Squidex.Web; |
|||
|
|||
namespace Squidex.Areas.Api.Controllers.Users.Models |
|||
{ |
|||
public sealed class ResourcesDto : Resource |
|||
{ |
|||
public static ResourcesDto FromController(ApiController controller) |
|||
{ |
|||
var result = new ResourcesDto(); |
|||
|
|||
if (controller.HasPermission(Permissions.AdminEventsRead)) |
|||
{ |
|||
result.AddGetLink("admin/eventConsumers", controller.Url<EventConsumersController>(x => nameof(x.GetEventConsumers))); |
|||
} |
|||
|
|||
if (controller.HasPermission(Permissions.AdminRestoreRead)) |
|||
{ |
|||
result.AddGetLink("admin/restore", controller.Url<RestoreController>(x => nameof(x.GetJob))); |
|||
} |
|||
|
|||
if (controller.HasPermission(Permissions.AdminUsersRead)) |
|||
{ |
|||
result.AddGetLink("admin/users", controller.Url<UserManagementController>(x => nameof(x.GetUsers))); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -1,145 +0,0 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Permission } from './permission'; |
|||
|
|||
describe('Permission', () => { |
|||
it('should check when permissions are not equal', () => { |
|||
const g = new Permission('app.contents'); |
|||
const r = new Permission('app.assets'); |
|||
|
|||
expect(g.allows(r)).toBeFalsy(); |
|||
|
|||
expect(g.includes(r)).toBeFalsy(); |
|||
}); |
|||
|
|||
it('should check when permissions are equal with wildcards', () => { |
|||
const g = new Permission('app.*'); |
|||
const r = new Permission('app.*'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
|
|||
expect(g.includes(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should check when equal permissions', () => { |
|||
const g = new Permission('app.contents'); |
|||
const r = new Permission('app.contents'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
|
|||
expect(g.includes(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should check when given is parent of requested', () => { |
|||
const g = new Permission('app'); |
|||
const r = new Permission('app.contents'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
|
|||
expect(g.includes(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should check when requested is parent of given', () => { |
|||
const g = new Permission('app.contents'); |
|||
const r = new Permission('app'); |
|||
|
|||
expect(g.allows(r)).toBeFalsy(); |
|||
|
|||
expect(g.includes(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should check when given is wildcard of requested', () => { |
|||
const g = new Permission('app.*'); |
|||
const r = new Permission('app.contents'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
|
|||
expect(g.includes(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should check when requested is wildcard of given', () => { |
|||
const g = new Permission('app.contents'); |
|||
const r = new Permission('app.*'); |
|||
|
|||
expect(g.allows(r)).toBeFalsy(); |
|||
|
|||
expect(g.includes(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should check when given is has alternatives of requested', () => { |
|||
const g = new Permission('app.contents|schemas'); |
|||
const r = new Permission('app.contents'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
|
|||
expect(g.includes(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should check when requested is has alternatives of given', () => { |
|||
const g = new Permission('app.contents'); |
|||
const r = new Permission('app.contents|schemas'); |
|||
|
|||
expect(g.allows(r)).toBeTruthy(); |
|||
|
|||
expect(g.includes(r)).toBeTruthy(); |
|||
}); |
|||
|
|||
|
|||
it('should check for requested is null', () => { |
|||
const g = new Permission('app.contents'); |
|||
|
|||
expect(g.allows(null!)).toBeFalsy(); |
|||
|
|||
expect(g.includes(null!)).toBeFalsy(); |
|||
}); |
|||
|
|||
it('should return true if any permission gives permission to requested', () => { |
|||
const set = [ |
|||
new Permission('app.contents'), |
|||
new Permission('app.assets') |
|||
]; |
|||
|
|||
expect(new Permission('app.contents').allowedBy(set)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should return true if any permission includes parent given', () => { |
|||
const set = [ |
|||
new Permission('app.contents'), |
|||
new Permission('app.assets') |
|||
]; |
|||
|
|||
expect(new Permission('app').includedIn(set)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should return true if any permission includes child given', () => { |
|||
const set = [ |
|||
new Permission('app.contents'), |
|||
new Permission('app.assets') |
|||
]; |
|||
|
|||
expect(new Permission('app.contents.read').includedIn(set)).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should return false if none permission gives permission to requested', () => { |
|||
const set = [ |
|||
new Permission('app.contents'), |
|||
new Permission('app.assets') |
|||
]; |
|||
|
|||
expect(new Permission('app.schemas').allowedBy(set)).toBeFalsy(); |
|||
}); |
|||
|
|||
it('should return false if none permission includes given', () => { |
|||
const set = [ |
|||
new Permission('app.contents'), |
|||
new Permission('app.assets') |
|||
]; |
|||
|
|||
expect(new Permission('other').allowedBy(set)).toBeFalsy(); |
|||
}); |
|||
}); |
|||
@ -1,116 +0,0 @@ |
|||
/* |
|||
* 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 includedIn(permissions: Permission[]) { |
|||
for (let permission of permissions) { |
|||
if (permission.includes(this)) { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public allowedBy(permissions: Permission[]) { |
|||
for (let permission of permissions) { |
|||
if (permission.allows(this)) { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public includes(permission?: Permission | string) { |
|||
if (!permission) { |
|||
return false; |
|||
} |
|||
|
|||
if (Types.isString(permission)) { |
|||
permission = new Permission(permission); |
|||
} |
|||
|
|||
for (let i = 0; i < Math.min(permission.parts.length, this.parts.length); i++) { |
|||
const lhs = this.parts[i]; |
|||
const rhs = permission.parts[i]; |
|||
|
|||
if (lhs != null && rhs != null && !Permission.intersects(lhs, rhs)) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
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++) { |
|||
const lhs = this.parts[i]; |
|||
const rhs = permission.parts[i]; |
|||
|
|||
if (lhs !== null && (rhs === null || !Permission.intersects(lhs, rhs))) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private static intersects(lhs: { [key: string]: true }, rhs: { [key: string]: true }) { |
|||
for (let key in lhs) { |
|||
if (lhs.hasOwnProperty(key)) { |
|||
if (rhs[key]) { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
for (let key in rhs) { |
|||
if (rhs.hasOwnProperty(key)) { |
|||
if (lhs[key]) { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
@ -1,133 +0,0 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { ChangeDetectorRef, Directive, Input, OnChanges, OnInit, TemplateRef, ViewContainerRef } from '@angular/core'; |
|||
|
|||
import { |
|||
AppDto, |
|||
AppsState, |
|||
AuthService, |
|||
Permission, |
|||
ResourceOwner, |
|||
SchemaDto, |
|||
SchemasState |
|||
} from '@app/shared/internal'; |
|||
|
|||
@Directive({ |
|||
selector: '[sqxPermission]' |
|||
}) |
|||
export class PermissionDirective extends ResourceOwner implements OnChanges, OnInit { |
|||
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 changeDetector: ChangeDetectorRef, |
|||
private readonly schemasState: SchemasState, |
|||
private readonly templateRef: TemplateRef<any>, |
|||
private readonly viewContainer: ViewContainerRef |
|||
) { |
|||
super(); |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.own( |
|||
this.appsState.selectedApp.subscribe(app => { |
|||
if (app && !this.app) { |
|||
this.update(app, this.schemasState.snapshot.selectedSchema); |
|||
} |
|||
})); |
|||
|
|||
this.own( |
|||
this.schemasState.selectedSchema.subscribe(schema => { |
|||
if (schema && !this.schema) { |
|||
this.update(this.appsState.snapshot.selectedApp, schema); |
|||
} |
|||
})); |
|||
} |
|||
|
|||
public ngOnChanges() { |
|||
this.update(this.appsState.snapshot.selectedApp, this.schemasState.snapshot.selectedSchema); |
|||
} |
|||
|
|||
private update(app?: AppDto | null, schema?: SchemaDto | null) { |
|||
if (this.app) { |
|||
app = this.app; |
|||
} |
|||
|
|||
if (this.schema) { |
|||
schema = this.schema; |
|||
} |
|||
|
|||
let permissions = this.permissions; |
|||
|
|||
let show = false; |
|||
|
|||
if (permissions) { |
|||
let include = permissions[0] === '?'; |
|||
|
|||
if (include) { |
|||
permissions = permissions.substr(1); |
|||
} |
|||
|
|||
const array = permissions.split(';'); |
|||
|
|||
for (let id of array) { |
|||
if (app) { |
|||
id = id.replace('{app}', app.name); |
|||
} |
|||
|
|||
if (schema) { |
|||
id = id.replace('{name}', schema.name); |
|||
} |
|||
|
|||
const permission = new Permission(id); |
|||
|
|||
if (include) { |
|||
if (app && permission.includedIn(app.permissions)) { |
|||
show = true; |
|||
} |
|||
|
|||
if (!show) { |
|||
show = permission.includedIn(this.authService.user!.permissions); |
|||
} |
|||
} else { |
|||
if (app && permission.allowedBy(app.permissions)) { |
|||
show = true; |
|||
} |
|||
|
|||
if (!show) { |
|||
show = permission.allowedBy(this.authService.user!.permissions); |
|||
} |
|||
} |
|||
|
|||
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; |
|||
} |
|||
|
|||
this.changeDetector.markForCheck(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue