mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-28 18:24:38 -05:00
Refactor permissions API endpoints, UI group permissions
This commit is contained in:
@@ -81,10 +81,10 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS)
|
||||
) {
|
||||
if (
|
||||
this.permissionsService.currentUserCan({
|
||||
action: PermissionAction.View,
|
||||
type: PermissionType.Document,
|
||||
})
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.Document
|
||||
)
|
||||
) {
|
||||
this.toastService.show({
|
||||
title: $localize`Document added`,
|
||||
@@ -246,10 +246,10 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
public get dragDropEnabled(): boolean {
|
||||
return (
|
||||
!this.router.url.includes('dashboard') &&
|
||||
this.permissionsService.currentUserCan({
|
||||
action: PermissionAction.Add,
|
||||
type: PermissionType.Document,
|
||||
})
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.Add,
|
||||
PermissionType.Document
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -84,6 +84,10 @@ import { GroupEditDialogComponent } from './components/common/edit-dialog/group-
|
||||
import { PermissionsSelectComponent } from './components/common/permissions-select/permissions-select.component'
|
||||
import { MailAccountEditDialogComponent } from './components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component'
|
||||
import { MailRuleEditDialogComponent } from './components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component'
|
||||
import { PermissionsUserComponent } from './components/common/input/permissions-user/permissions-user.component'
|
||||
import { PermissionsGroupComponent } from './components/common/input/permissions-group/permissions-group.component'
|
||||
import { IfOwnerDirective } from './directives/if-owner.directive'
|
||||
import { IfObjectPermissionsDirective } from './directives/if-object-permissions.directive'
|
||||
|
||||
import localeBe from '@angular/common/locales/be'
|
||||
import localeCs from '@angular/common/locales/cs'
|
||||
@@ -104,9 +108,6 @@ import localeSr from '@angular/common/locales/sr'
|
||||
import localeSv from '@angular/common/locales/sv'
|
||||
import localeTr from '@angular/common/locales/tr'
|
||||
import localeZh from '@angular/common/locales/zh'
|
||||
import { ShareUserComponent } from './components/common/input/share-user/share-user.component'
|
||||
import { IfOwnerDirective } from './directives/if-owner.directive'
|
||||
import { IfObjectPermissionsDirective } from './directives/if-object-permissions.directive'
|
||||
|
||||
registerLocaleData(localeBe)
|
||||
registerLocaleData(localeCs)
|
||||
@@ -198,7 +199,8 @@ function initializeApp(settings: SettingsService) {
|
||||
PermissionsSelectComponent,
|
||||
MailAccountEditDialogComponent,
|
||||
MailRuleEditDialogComponent,
|
||||
ShareUserComponent,
|
||||
PermissionsUserComponent,
|
||||
PermissionsGroupComponent,
|
||||
IfOwnerDirective,
|
||||
IfObjectPermissionsDirective,
|
||||
],
|
||||
|
@@ -11,11 +11,19 @@
|
||||
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
|
||||
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></app-input-check>
|
||||
|
||||
<div *ifOwner="object?.owner">
|
||||
<div *ifOwner="object">
|
||||
<h5 i18n>Permissions</h5>
|
||||
<div formGroupName="set_permissions">
|
||||
<app-share-user type="view" formControlName="view"></app-share-user>
|
||||
<app-share-user type="change" formControlName="change"></app-share-user>
|
||||
<h6 i18n>View</h6>
|
||||
<div formGroupName="view">
|
||||
<app-permissions-user type="view" formControlName="users"></app-permissions-user>
|
||||
<app-permissions-group type="view" formControlName="groups"></app-permissions-group>
|
||||
</div>
|
||||
<h6 i18n>Edit</h6>
|
||||
<div formGroupName="change">
|
||||
<app-permissions-user type="change" formControlName="users"></app-permissions-user>
|
||||
<app-permissions-group type="change" formControlName="groups"></app-permissions-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -31,8 +31,14 @@ export class CorrespondentEditDialogComponent extends EditDialogComponent<Paperl
|
||||
match: new FormControl(''),
|
||||
is_insensitive: new FormControl(true),
|
||||
set_permissions: new FormGroup({
|
||||
view: new FormControl(null),
|
||||
change: new FormControl(null),
|
||||
view: new FormGroup({
|
||||
users: new FormControl(null),
|
||||
groups: new FormControl(null),
|
||||
}),
|
||||
change: new FormGroup({
|
||||
users: new FormControl(null),
|
||||
groups: new FormControl(null),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
@@ -11,11 +11,19 @@
|
||||
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
|
||||
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check>
|
||||
|
||||
<div *ifOwner="object?.owner">
|
||||
<div *ifOwner="object">
|
||||
<h5 i18n>Permissions</h5>
|
||||
<div formGroupName="set_permissions">
|
||||
<app-share-user type="view" formControlName="view"></app-share-user>
|
||||
<app-share-user type="change" formControlName="change"></app-share-user>
|
||||
<h6 i18n>View</h6>
|
||||
<div formGroupName="view">
|
||||
<app-permissions-user type="view" formControlName="users"></app-permissions-user>
|
||||
<app-permissions-group type="view" formControlName="groups"></app-permissions-group>
|
||||
</div>
|
||||
<h6 i18n>Edit</h6>
|
||||
<div formGroupName="change">
|
||||
<app-permissions-user type="change" formControlName="users"></app-permissions-user>
|
||||
<app-permissions-group type="change" formControlName="groups"></app-permissions-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -31,8 +31,14 @@ export class DocumentTypeEditDialogComponent extends EditDialogComponent<Paperle
|
||||
match: new FormControl(''),
|
||||
is_insensitive: new FormControl(true),
|
||||
set_permissions: new FormGroup({
|
||||
view: new FormControl(null),
|
||||
change: new FormControl(null),
|
||||
view: new FormGroup({
|
||||
users: new FormControl(null),
|
||||
groups: new FormControl(null),
|
||||
}),
|
||||
change: new FormGroup({
|
||||
users: new FormControl(null),
|
||||
groups: new FormControl(null),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
@@ -39,14 +39,7 @@ export abstract class EditDialogComponent<
|
||||
ngOnInit(): void {
|
||||
if (this.object != null) {
|
||||
if (this.object['permissions']) {
|
||||
this.object['set_permissions'] = {
|
||||
view: (this.object as ObjectWithPermissions).permissions
|
||||
.filter((p) => (p[1] as string).includes('view'))
|
||||
.map((p) => p[0]),
|
||||
change: (this.object as ObjectWithPermissions).permissions
|
||||
.filter((p) => (p[1] as string).includes('change'))
|
||||
.map((p) => p[0]),
|
||||
}
|
||||
this.object['set_permissions'] = this.object['permissions']
|
||||
}
|
||||
this.objectForm.patchValue(this.object)
|
||||
}
|
||||
|
@@ -16,11 +16,19 @@
|
||||
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
|
||||
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check>
|
||||
|
||||
<div *ifOwner="object?.owner">
|
||||
<div *ifOwner="object">
|
||||
<h5 i18n>Permissions</h5>
|
||||
<div formGroupName="set_permissions">
|
||||
<app-share-user type="view" formControlName="view"></app-share-user>
|
||||
<app-share-user type="change" formControlName="change"></app-share-user>
|
||||
<h6 i18n>View</h6>
|
||||
<div formGroupName="view">
|
||||
<app-permissions-user type="view" formControlName="users"></app-permissions-user>
|
||||
<app-permissions-group type="view" formControlName="groups"></app-permissions-group>
|
||||
</div>
|
||||
<h6 i18n>Edit</h6>
|
||||
<div formGroupName="change">
|
||||
<app-permissions-user type="change" formControlName="users"></app-permissions-user>
|
||||
<app-permissions-group type="change" formControlName="groups"></app-permissions-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -42,8 +42,14 @@ export class StoragePathEditDialogComponent extends EditDialogComponent<Paperles
|
||||
match: new FormControl(''),
|
||||
is_insensitive: new FormControl(true),
|
||||
set_permissions: new FormGroup({
|
||||
view: new FormControl(null),
|
||||
change: new FormControl(null),
|
||||
view: new FormGroup({
|
||||
users: new FormControl(null),
|
||||
groups: new FormControl(null),
|
||||
}),
|
||||
change: new FormGroup({
|
||||
users: new FormControl(null),
|
||||
groups: new FormControl(null),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
@@ -14,11 +14,19 @@
|
||||
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
|
||||
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check>
|
||||
|
||||
<div *ifOwner="object?.owner">
|
||||
<div *ifOwner="object">
|
||||
<h5 i18n>Permissions</h5>
|
||||
<div formGroupName="set_permissions">
|
||||
<app-share-user type="view" formControlName="view"></app-share-user>
|
||||
<app-share-user type="change" formControlName="change"></app-share-user>
|
||||
<h6 i18n>View</h6>
|
||||
<div formGroupName="view">
|
||||
<app-permissions-user type="view" formControlName="users"></app-permissions-user>
|
||||
<app-permissions-group type="view" formControlName="groups"></app-permissions-group>
|
||||
</div>
|
||||
<h6 i18n>Edit</h6>
|
||||
<div formGroupName="change">
|
||||
<app-permissions-user type="change" formControlName="users"></app-permissions-user>
|
||||
<app-permissions-group type="change" formControlName="groups"></app-permissions-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -34,8 +34,14 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
|
||||
match: new FormControl(''),
|
||||
is_insensitive: new FormControl(true),
|
||||
set_permissions: new FormGroup({
|
||||
view: new FormControl(null),
|
||||
change: new FormControl(null),
|
||||
view: new FormGroup({
|
||||
users: new FormControl(null),
|
||||
groups: new FormControl(null),
|
||||
}),
|
||||
change: new FormGroup({
|
||||
users: new FormControl(null),
|
||||
groups: new FormControl(null),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
@@ -0,0 +1,15 @@
|
||||
<div class="mb-3 paperless-input-select">
|
||||
<label class="form-label" [for]="inputId">{{title}}</label>
|
||||
<div>
|
||||
<ng-select name="inputId" [(ngModel)]="value"
|
||||
[disabled]="disabled"
|
||||
clearable="true"
|
||||
[items]="groups"
|
||||
multiple="true"
|
||||
bindLabel="name"
|
||||
bindValue="id"
|
||||
(change)="onChange(value)">
|
||||
</ng-select>
|
||||
</div>
|
||||
<small *ngIf="hint" class="form-text text-muted">{{hint}}</small>
|
||||
</div>
|
@@ -0,0 +1,48 @@
|
||||
import { Component, forwardRef, Input, OnInit } from '@angular/core'
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||
import { first } from 'rxjs/operators'
|
||||
import { PaperlessGroup } from 'src/app/data/paperless-group'
|
||||
import { GroupService } from 'src/app/services/rest/group.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { AbstractInputComponent } from '../abstract-input'
|
||||
|
||||
@Component({
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => PermissionsGroupComponent),
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
selector: 'app-permissions-group',
|
||||
templateUrl: './permissions-group.component.html',
|
||||
styleUrls: ['./permissions-group.component.scss'],
|
||||
})
|
||||
export class PermissionsGroupComponent
|
||||
extends AbstractInputComponent<PaperlessGroup>
|
||||
implements OnInit
|
||||
{
|
||||
groups: PaperlessGroup[]
|
||||
|
||||
@Input()
|
||||
type: string
|
||||
|
||||
constructor(groupService: GroupService, settings: SettingsService) {
|
||||
super()
|
||||
groupService
|
||||
.listAll()
|
||||
.pipe(first())
|
||||
.subscribe((result) => (this.groups = result.results))
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.type == 'view') {
|
||||
this.title = $localize`Groups can view`
|
||||
} else if (this.type == 'change') {
|
||||
this.title = $localize`Groups can edit`
|
||||
this.hint = $localize`Edit permissions also grant viewing permissions`
|
||||
}
|
||||
|
||||
super.ngOnInit()
|
||||
}
|
||||
}
|
@@ -3,21 +3,22 @@ import { NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||
import { first } from 'rxjs/operators'
|
||||
import { PaperlessUser } from 'src/app/data/paperless-user'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { AbstractInputComponent } from '../abstract-input'
|
||||
|
||||
@Component({
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => ShareUserComponent),
|
||||
useExisting: forwardRef(() => PermissionsUserComponent),
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
selector: 'app-share-user',
|
||||
templateUrl: './share-user.component.html',
|
||||
styleUrls: ['./share-user.component.scss'],
|
||||
selector: 'app-permissions-user',
|
||||
templateUrl: './permissions-user.component.html',
|
||||
styleUrls: ['./permissions-user.component.scss'],
|
||||
})
|
||||
export class ShareUserComponent
|
||||
export class PermissionsUserComponent
|
||||
extends AbstractInputComponent<PaperlessUser>
|
||||
implements OnInit
|
||||
{
|
||||
@@ -26,12 +27,17 @@ export class ShareUserComponent
|
||||
@Input()
|
||||
type: string
|
||||
|
||||
constructor(userService: UserService) {
|
||||
constructor(userService: UserService, settings: SettingsService) {
|
||||
super()
|
||||
userService
|
||||
.listAll()
|
||||
.pipe(first())
|
||||
.subscribe((result) => (this.users = result.results))
|
||||
.subscribe(
|
||||
(result) =>
|
||||
(this.users = result.results.filter(
|
||||
(u) => u.id !== settings.currentUser.id
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
@@ -156,18 +156,18 @@ export class PermissionsSelectComponent
|
||||
if (this._inheritedPermissions.length == 0) return false
|
||||
else if (actionKey) {
|
||||
return this._inheritedPermissions.includes(
|
||||
this.permissionsService.getPermissionCode({
|
||||
action: PermissionAction[actionKey],
|
||||
type: PermissionType[typeKey],
|
||||
})
|
||||
this.permissionsService.getPermissionCode(
|
||||
PermissionAction[actionKey],
|
||||
PermissionType[typeKey]
|
||||
)
|
||||
)
|
||||
} else {
|
||||
return Object.values(PermissionAction).every((action) => {
|
||||
return this._inheritedPermissions.includes(
|
||||
this.permissionsService.getPermissionCode({
|
||||
action: action as PermissionAction,
|
||||
type: PermissionType[typeKey],
|
||||
})
|
||||
this.permissionsService.getPermissionCode(
|
||||
action as PermissionAction,
|
||||
PermissionType[typeKey]
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<div class="input-group-text" i18n>of {{previewNumPages}}</div>
|
||||
</div>
|
||||
|
||||
<button *ifOwner="document?.owner" type="button" class="btn btn-sm btn-outline-danger me-2 ms-auto" (click)="delete()">
|
||||
<button *ifOwner="document" type="button" class="btn btn-sm btn-outline-danger me-2 ms-auto" (click)="delete()">
|
||||
<svg class="buttonicon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#trash" />
|
||||
</svg><span class="d-none d-lg-inline ps-1" i18n>Delete</span>
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<button *ifOwner="document?.owner" type="button" class="btn btn-sm btn-outline-primary me-2" (click)="redoOcr()">
|
||||
<button *ifOwner="document" type="button" class="btn btn-sm btn-outline-primary me-2" (click)="redoOcr()">
|
||||
<svg class="buttonicon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#arrow-counterclockwise" />
|
||||
</svg><span class="d-none d-lg-inline ps-1" i18n>Redo OCR</span>
|
||||
@@ -178,12 +178,20 @@
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li [ngbNavItem]="6" *ifOwner="document?.owner">
|
||||
<li [ngbNavItem]="6" *ifOwner="document">
|
||||
<a ngbNavLink i18n>Permissions</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div formGroupName="set_permissions">
|
||||
<app-share-user type="view" formControlName="view"></app-share-user>
|
||||
<app-share-user type="change" formControlName="change"></app-share-user>
|
||||
<h6 i18n>View</h6>
|
||||
<div formGroupName="view">
|
||||
<app-permissions-user type="view" formControlName="users"></app-permissions-user>
|
||||
<app-permissions-group type="view" formControlName="groups"></app-permissions-group>
|
||||
</div>
|
||||
<h6 i18n>Edit</h6>
|
||||
<div formGroupName="change">
|
||||
<app-permissions-user type="change" formControlName="users"></app-permissions-user>
|
||||
<app-permissions-group type="change" formControlName="groups"></app-permissions-group>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
@@ -191,7 +199,7 @@
|
||||
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
|
||||
<ng-container action="PermissionAction.Change" *ifObjectPermissions="document">
|
||||
<ng-container *ifObjectPermissions="{ object: document, action: PermissionAction.Change }">
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="discard()" i18n [disabled]="networkActive || !(isDirty$ | async)">Discard</button>
|
||||
<button type="button" class="btn btn-outline-primary" (click)="saveEditNext()" *ngIf="hasNext()" i18n [disabled]="networkActive || !(isDirty$ | async) || error">Save & next</button>
|
||||
<button type="submit" class="btn btn-primary" *ifPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }" i18n [disabled]="networkActive || !(isDirty$ | async) || error">Save</button>
|
||||
|
@@ -85,8 +85,14 @@ export class DocumentDetailComponent
|
||||
archive_serial_number: new FormControl(),
|
||||
tags: new FormControl([]),
|
||||
set_permissions: new FormGroup({
|
||||
view: new FormControl(null),
|
||||
change: new FormControl(null),
|
||||
view: new FormGroup({
|
||||
users: new FormControl(null),
|
||||
groups: new FormControl(null),
|
||||
}),
|
||||
change: new FormGroup({
|
||||
users: new FormControl(null),
|
||||
groups: new FormControl(null),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -235,14 +241,7 @@ export class DocumentDetailComponent
|
||||
storage_path: doc.storage_path,
|
||||
archive_serial_number: doc.archive_serial_number,
|
||||
tags: [...doc.tags],
|
||||
set_permissions: {
|
||||
view: doc.permissions
|
||||
.filter((p) => (p[1] as string).includes('view'))
|
||||
.map((p) => p[0]),
|
||||
change: doc.permissions
|
||||
.filter((p) => (p[1] as string).includes('change'))
|
||||
.map((p) => p[0]),
|
||||
},
|
||||
set_permissions: doc.permissions,
|
||||
})
|
||||
|
||||
this.isDirty$ = dirtyCheck(
|
||||
@@ -297,14 +296,7 @@ export class DocumentDetailComponent
|
||||
},
|
||||
})
|
||||
this.title = this.documentTitlePipe.transform(doc.title)
|
||||
doc['set_permissions'] = {
|
||||
view: doc.permissions
|
||||
.filter((p) => (p[1] as string).includes('view'))
|
||||
.map((p) => p[0]),
|
||||
change: doc.permissions
|
||||
.filter((p) => (p[1] as string).includes('change'))
|
||||
.map((p) => p[0]),
|
||||
}
|
||||
doc['set_permissions'] = doc.permissions
|
||||
this.documentForm.patchValue(doc)
|
||||
if (!this.userCanEdit) this.documentForm.disable()
|
||||
}
|
||||
@@ -586,10 +578,10 @@ export class DocumentDetailComponent
|
||||
get commentsEnabled(): boolean {
|
||||
return (
|
||||
this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED) &&
|
||||
this.permissionsService.currentUserCan({
|
||||
action: PermissionAction.View,
|
||||
type: PermissionType.Document,
|
||||
})
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.Document
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -222,9 +222,7 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
|
||||
}
|
||||
|
||||
userCanDelete(object: ObjectWithPermissions): boolean {
|
||||
return (
|
||||
!object.owner || this.permissionsService.currentUserIsOwner(object.owner)
|
||||
)
|
||||
return this.permissionsService.currentUserOwnsObject(object)
|
||||
}
|
||||
|
||||
userCanEdit(object: ObjectWithPermissions): boolean {
|
||||
|
@@ -1,8 +1,19 @@
|
||||
import { ObjectWithId } from './object-with-id'
|
||||
import { PaperlessUser } from './paperless-user'
|
||||
|
||||
export interface PermissionsObject {
|
||||
view: {
|
||||
users: Array<number>
|
||||
groups: Array<number>
|
||||
}
|
||||
change: {
|
||||
users: Array<number>
|
||||
groups: Array<number>
|
||||
}
|
||||
}
|
||||
|
||||
export interface ObjectWithPermissions extends ObjectWithId {
|
||||
owner?: PaperlessUser
|
||||
|
||||
permissions?: Array<[number, string]>
|
||||
permissions?: PermissionsObject
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
Directive,
|
||||
EmbeddedViewRef,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
@@ -18,10 +19,12 @@ import {
|
||||
export class IfObjectPermissionsDirective implements OnInit, OnChanges {
|
||||
// The role the user must have
|
||||
@Input()
|
||||
ifObjectPermissions: ObjectWithPermissions
|
||||
ifObjectPermissions: {
|
||||
object: ObjectWithPermissions
|
||||
action: PermissionAction
|
||||
}
|
||||
|
||||
@Input()
|
||||
action: PermissionAction
|
||||
createdView: EmbeddedViewRef<any>
|
||||
|
||||
/**
|
||||
* @param {ViewContainerRef} viewContainerRef -- The location where we need to render the templateRef
|
||||
@@ -36,13 +39,16 @@ export class IfObjectPermissionsDirective implements OnInit, OnChanges {
|
||||
|
||||
public ngOnInit(): void {
|
||||
if (
|
||||
!this.ifObjectPermissions ||
|
||||
!this.ifObjectPermissions?.object ||
|
||||
this.permissionsService.currentUserHasObjectPermissions(
|
||||
this.action,
|
||||
this.ifObjectPermissions
|
||||
this.ifObjectPermissions.action,
|
||||
this.ifObjectPermissions.object
|
||||
)
|
||||
) {
|
||||
this.viewContainerRef.createEmbeddedView(this.templateRef)
|
||||
if (!this.createdView)
|
||||
this.createdView = this.viewContainerRef.createEmbeddedView(
|
||||
this.templateRef
|
||||
)
|
||||
} else {
|
||||
this.viewContainerRef.clear()
|
||||
}
|
||||
|
@@ -1,12 +1,13 @@
|
||||
import {
|
||||
Directive,
|
||||
EmbeddedViewRef,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
TemplateRef,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core'
|
||||
import { PaperlessUser } from '../data/paperless-user'
|
||||
import { ObjectWithPermissions } from '../data/object-with-permissions'
|
||||
import { PermissionsService } from '../services/permissions.service'
|
||||
|
||||
@Directive({
|
||||
@@ -15,7 +16,9 @@ import { PermissionsService } from '../services/permissions.service'
|
||||
export class IfOwnerDirective implements OnInit, OnChanges {
|
||||
// The role the user must have
|
||||
@Input()
|
||||
ifOwner: PaperlessUser
|
||||
ifOwner: ObjectWithPermissions
|
||||
|
||||
createdView: EmbeddedViewRef<any>
|
||||
|
||||
/**
|
||||
* @param {ViewContainerRef} viewContainerRef -- The location where we need to render the templateRef
|
||||
@@ -29,11 +32,11 @@ export class IfOwnerDirective implements OnInit, OnChanges {
|
||||
) {}
|
||||
|
||||
public ngOnInit(): void {
|
||||
if (
|
||||
!this.ifOwner ||
|
||||
this.permissionsService.currentUserIsOwner(this.ifOwner)
|
||||
) {
|
||||
this.viewContainerRef.createEmbeddedView(this.templateRef)
|
||||
if (this.permissionsService.currentUserOwnsObject(this.ifOwner)) {
|
||||
if (!this.createdView)
|
||||
this.createdView = this.viewContainerRef.createEmbeddedView(
|
||||
this.templateRef
|
||||
)
|
||||
} else {
|
||||
this.viewContainerRef.clear()
|
||||
}
|
||||
|
@@ -6,17 +6,19 @@ import {
|
||||
TemplateRef,
|
||||
} from '@angular/core'
|
||||
import {
|
||||
PaperlessPermission,
|
||||
PermissionAction,
|
||||
PermissionsService,
|
||||
PermissionType,
|
||||
} from '../services/permissions.service'
|
||||
|
||||
@Directive({
|
||||
selector: '[ifPermissions]',
|
||||
})
|
||||
export class IfPermissionsDirective implements OnInit {
|
||||
// The role the user must have
|
||||
@Input()
|
||||
ifPermissions: Array<PaperlessPermission> | PaperlessPermission
|
||||
ifPermissions:
|
||||
| Array<{ action: PermissionAction; type: PermissionType }>
|
||||
| { action: PermissionAction; type: PermissionType }
|
||||
|
||||
/**
|
||||
* @param {ViewContainerRef} viewContainerRef -- The location where we need to render the templateRef
|
||||
@@ -33,8 +35,8 @@ export class IfPermissionsDirective implements OnInit {
|
||||
if (
|
||||
[]
|
||||
.concat(this.ifPermissions)
|
||||
.every((perm: PaperlessPermission) =>
|
||||
this.permissionsService.currentUserCan(perm)
|
||||
.every((perm: { action: PermissionAction; type: PermissionType }) =>
|
||||
this.permissionsService.currentUserCan(perm.action, perm.type)
|
||||
)
|
||||
) {
|
||||
this.viewContainerRef.createEmbeddedView(this.templateRef)
|
||||
|
@@ -22,7 +22,10 @@ export class PermissionsGuard implements CanActivate {
|
||||
state: RouterStateSnapshot
|
||||
): boolean | UrlTree {
|
||||
if (
|
||||
!this.permissionsService.currentUserCan(route.data.requiredPermission)
|
||||
!this.permissionsService.currentUserCan(
|
||||
route.data.requiredPermission.action,
|
||||
route.data.requiredPermission.type
|
||||
)
|
||||
) {
|
||||
this.toastService.showError(
|
||||
$localize`You don't have permissions to do that`
|
||||
|
@@ -25,11 +25,6 @@ export enum PermissionType {
|
||||
Admin = '%s_logentry',
|
||||
}
|
||||
|
||||
export interface PaperlessPermission {
|
||||
action: PermissionAction
|
||||
type: PermissionType
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
@@ -42,25 +37,34 @@ export class PermissionsService {
|
||||
this.currentUser = currentUser
|
||||
}
|
||||
|
||||
public currentUserCan(permission: PaperlessPermission): boolean {
|
||||
return this.permissions.includes(this.getPermissionCode(permission))
|
||||
public currentUserCan(
|
||||
action: PermissionAction,
|
||||
type: PermissionType
|
||||
): boolean {
|
||||
return this.permissions.includes(this.getPermissionCode(action, type))
|
||||
}
|
||||
|
||||
public currentUserIsOwner(owner: PaperlessUser): boolean {
|
||||
return owner?.id === this.currentUser.id
|
||||
public currentUserOwnsObject(object: ObjectWithPermissions): boolean {
|
||||
return !object || !object.owner || object.owner.id === this.currentUser.id
|
||||
}
|
||||
|
||||
public currentUserHasObjectPermissions(
|
||||
action: string,
|
||||
object: ObjectWithPermissions
|
||||
): boolean {
|
||||
return (object.permissions[action] as Array<number>)?.includes(
|
||||
this.currentUser.id
|
||||
return (
|
||||
this.currentUserOwnsObject(object) ||
|
||||
(object.permissions[action]['users'] as Array<number>)?.includes(
|
||||
this.currentUser.id
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
public getPermissionCode(permission: PaperlessPermission): string {
|
||||
return permission.type.replace('%s', permission.action)
|
||||
public getPermissionCode(
|
||||
action: PermissionAction,
|
||||
type: PermissionType
|
||||
): string {
|
||||
return type.replace('%s', action)
|
||||
}
|
||||
|
||||
public getPermissionKeys(permissionStr: string): {
|
||||
|
Reference in New Issue
Block a user