(ListingModes.table);
constructor(
@@ -155,7 +158,7 @@ export class ConfigService {
label: this._translateService.instant('dossier-overview.header-actions.edit'),
action: $event => this._openEditDossierDialog($event, dossierId),
icon: 'iqser:edit',
- hide: !this._iqserPermissionsService.has(ROLES.dossiers.edit),
+ hide: !this.currentUser.isManager && !this._iqserPermissionsService.has(ROLES.dossiers.edit),
helpModeKey: 'edit_dossier_in_dossier',
disabled$,
},
diff --git a/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html b/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html
index 9c064c31a..adc772359 100644
--- a/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html
+++ b/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html
@@ -1,12 +1,12 @@
();
analysisForced: boolean;
files: File[];
diff --git a/apps/red-ui/src/app/modules/dossiers-listing/dossiers-listing.module.ts b/apps/red-ui/src/app/modules/dossiers-listing/dossiers-listing.module.ts
index 087ea8f0c..73bc03d10 100644
--- a/apps/red-ui/src/app/modules/dossiers-listing/dossiers-listing.module.ts
+++ b/apps/red-ui/src/app/modules/dossiers-listing/dossiers-listing.module.ts
@@ -9,6 +9,7 @@ import {
IqserScrollbarModule,
IqserSharedModule,
IqserUsersModule,
+ LogPipe,
} from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core';
import { DossiersListingScreenComponent } from './screen/dossiers-listing-screen.component';
@@ -58,6 +59,7 @@ const routes: Routes = [
IqserScrollbarModule,
IqserSharedModule,
IqserPermissionsModule,
+ LogPipe,
],
})
export class DossiersListingModule {}
diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html b/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html
index 06bf12305..714e1343b 100644
--- a/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html
+++ b/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html
@@ -82,7 +82,7 @@
{
private readonly _toaster: Toaster,
private readonly _logger: NGXLogger,
private readonly _permissionsService: PermissionsService,
+ private readonly _iqaerPermissionsService: IqserPermissionsService,
private readonly _activeDossiersService: ActiveDossiersService,
) {
super();
@@ -93,11 +96,17 @@ export class ManualRedactionService extends GenericService {
addAnnotation(requests: List, dossierId: string, fileId: string, dictionaryLabel?: string) {
const toast = requests[0].addToDictionary ? this.#showAddToDictionaryToast(requests, dictionaryLabel) : this.#showToast('add');
- if (this._permissionsService.isApprover(this.#dossier(dossierId))) {
+ const canAddRedaction = this._iqaerPermissionsService.has(ROLES.redactions.write);
+ if (canAddRedaction && this._permissionsService.isApprover(this.#dossier(dossierId))) {
return this.add(requests, dossierId, fileId).pipe(toast);
}
- return this.requestAdd(requests, dossierId, fileId).pipe(toast);
+ const canRequestRedaction = this._iqaerPermissionsService.has(ROLES.redactions.request);
+ if (canRequestRedaction) {
+ return this.requestAdd(requests, dossierId, fileId).pipe(toast);
+ }
+
+ return of(undefined);
}
bulkForce(requests: List, dossierId: string, fileId: string) {
diff --git a/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts
index f77fae7ac..dcd169ed6 100644
--- a/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts
+++ b/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts
@@ -5,9 +5,10 @@ import { PermissionsService } from '../../../services/permissions.service';
import { FilePreviewStateService } from './file-preview-state.service';
import { TranslateService } from '@ngx-translate/core';
import { AnnotationActionsService } from './annotation-actions.service';
-import { BASE_HREF_FN } from '@iqser/common-ui';
+import { BASE_HREF_FN, IqserPermissionsService } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IHeaderElement } from '@red/domain';
+import { ROLES } from '@users/roles';
@Injectable()
export class PdfAnnotationActionsService {
@@ -17,6 +18,7 @@ export class PdfAnnotationActionsService {
readonly #ngZone = inject(NgZone);
readonly #convertPath = inject(BASE_HREF_FN);
readonly #annotationActionsService = inject(AnnotationActionsService);
+ readonly #iqserPermissionsService = inject(IqserPermissionsService);
get(annotations: AnnotationWrapper[]): IHeaderElement[] {
const availableActions: IHeaderElement[] = [];
@@ -152,7 +154,9 @@ export class PdfAnnotationActionsService {
),
canAcceptRecommendation: permissions.reduce((acc, next) => acc && next.canAcceptRecommendation, true),
canAcceptSuggestion: permissions.reduce((acc, next) => acc && next.canAcceptSuggestion, true),
- canUndo: permissions.reduce((acc, next) => acc && next.canUndo, true),
+ canUndo:
+ this.#iqserPermissionsService.has(ROLES.redactions.deleteManual) &&
+ permissions.reduce((acc, next) => acc && next.canUndo, true),
canMarkAsFalsePositive: permissions.reduce((acc, next) => acc && next.canMarkAsFalsePositive, true),
canForceRedaction: permissions.reduce((acc, next) => acc && next.canForceRedaction, true),
canForceHint: permissions.reduce((acc, next) => acc && next.canForceHint, true),
diff --git a/apps/red-ui/src/app/modules/file-preview/services/pdf-proxy.service.ts b/apps/red-ui/src/app/modules/file-preview/services/pdf-proxy.service.ts
index 3732e9767..c6ed26bb6 100644
--- a/apps/red-ui/src/app/modules/file-preview/services/pdf-proxy.service.ts
+++ b/apps/red-ui/src/app/modules/file-preview/services/pdf-proxy.service.ts
@@ -9,7 +9,7 @@ import {
} from '@models/file/manual-redaction-entry.wrapper';
import { AnnotationDrawService } from '../../pdf-viewer/services/annotation-draw.service';
import { UserPreferenceService } from '@users/user-preference.service';
-import { BASE_HREF_FN, BaseHrefFn, shareDistinctLast } from '@iqser/common-ui';
+import { BASE_HREF_FN, BaseHrefFn, IqserPermissionsService, shareDistinctLast } from '@iqser/common-ui';
import { toPosition } from '../utils/pdf-calculation.utils';
import { MultiSelectService } from './multi-select.service';
import { FilePreviewStateService } from './file-preview-state.service';
@@ -28,6 +28,7 @@ import { PermissionsService } from '@services/permissions.service';
import { AnnotationsListingService } from './annotations-listing.service';
import { PdfAnnotationActionsService } from './pdf-annotation-actions.service';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
+import { ROLES } from '@users/roles';
import Annotation = Core.Annotations.Annotation;
import Quad = Core.Math.Quad;
@@ -70,6 +71,7 @@ export class PdfProxyService {
private readonly _listingService: AnnotationsListingService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _dictionariesMapService: DictionariesMapService,
+ private readonly _iqserPermissionsService: IqserPermissionsService,
) {
this.canPerformAnnotationActions$ = this.#canPerformAnnotationActions$;
}
@@ -240,20 +242,23 @@ export class PdfProxyService {
});
}
- popups.push({
- type: 'actionButton',
- dataElement: TextPopups.ADD_REDACTION,
- img: this.#addRedactionIcon,
- title: this.#getTitle(ManualRedactionEntryTypes.REDACTION),
- onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.REDACTION),
- });
- popups.push({
- type: 'actionButton',
- dataElement: TextPopups.ADD_DICTIONARY,
- img: this.#addDictIcon,
- title: this.#getTitle(ManualRedactionEntryTypes.DICTIONARY),
- onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.DICTIONARY),
- });
+ if (this._iqserPermissionsService.has(ROLES.redactions.write) || this._iqserPermissionsService.has(ROLES.redactions.request)) {
+ popups.push({
+ type: 'actionButton',
+ dataElement: TextPopups.ADD_REDACTION,
+ img: this.#addRedactionIcon,
+ title: this.#getTitle(ManualRedactionEntryTypes.REDACTION),
+ onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.REDACTION),
+ });
+
+ popups.push({
+ type: 'actionButton',
+ dataElement: TextPopups.ADD_DICTIONARY,
+ img: this.#addDictIcon,
+ title: this.#getTitle(ManualRedactionEntryTypes.DICTIONARY),
+ onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.DICTIONARY),
+ });
+ }
this._pdf.configureTextPopups(popups);
diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts
index fc11418ed..87104a423 100644
--- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts
+++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts
@@ -1,10 +1,17 @@
import { AfterViewInit, Component, Inject, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
-import { Dossier } from '@red/domain';
+import { Dossier, User } from '@red/domain';
import { EditDossierGeneralInfoComponent } from './general-info/edit-dossier-general-info.component';
import { EditDossierDownloadPackageComponent } from './download-package/edit-dossier-download-package.component';
import { EditDossierSectionInterface } from './edit-dossier-section.interface';
-import { BaseDialogComponent, ConfirmOptions, IconButtonTypes, IqserPermissionsService, SaveOptions } from '@iqser/common-ui';
+import {
+ BaseDialogComponent,
+ ConfirmOptions,
+ getCurrentUser,
+ IconButtonTypes,
+ IqserPermissionsService,
+ SaveOptions,
+} from '@iqser/common-ui';
import { EditDossierDictionaryComponent } from './dictionary/edit-dossier-dictionary.component';
import { EditDossierAttributesComponent } from './attributes/edit-dossier-attributes.component';
@@ -42,6 +49,7 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
@ViewChild(EditDossierDictionaryComponent) dictionaryComponent: EditDossierDictionaryComponent;
@ViewChild(EditDossierTeamComponent) membersComponent: EditDossierTeamComponent;
@ViewChild(EditDossierAttributesComponent) attributesComponent: EditDossierAttributesComponent;
+ readonly #currentUser = getCurrentUser();
private _dossier: Dossier;
constructor(
@@ -89,7 +97,9 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
get showActionButtons(): boolean {
return (
- (['members'].includes(this.activeNav) && this._iqserPermissionsService.has(ROLES.dossiers.edit)) ||
+ (['members'].includes(this.activeNav) &&
+ this.#currentUser.isManager &&
+ this._iqserPermissionsService.has(ROLES.dossiers.edit)) ||
this._permissionsService.canEditDossier(this._dossier)
);
}
diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts
index 813cbb526..717f2cc8e 100644
--- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts
+++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.ts
@@ -1,14 +1,14 @@
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { UserService } from '@users/user.service';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
-import { Dossier, IDossierRequest } from '@red/domain';
+import { Dossier, IDossierRequest, User } from '@red/domain';
import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { PermissionsService } from '@services/permissions.service';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { compareLists } from '@utils/functions';
import { FilesService } from '@services/files/files.service';
-import { ROLES } from '@users/roles';
+import { getCurrentUser } from '@iqser/common-ui';
@Component({
selector: 'redaction-edit-dossier-team',
@@ -23,9 +23,9 @@ export class EditDossierTeamComponent implements EditDossierSectionInterface, On
@Input() dossier: Dossier;
membersSelectOptions: string[] = [];
-
- readonly ownersSelectOptions = this.userService.all.filter(u => u.has(ROLES.dossiers.write) || u.isManager).map(m => m.id);
+ readonly ownersSelectOptions = this.userService.all.filter(u => u.isManager).map(m => m.id);
readonly selectedReviewers$ = new BehaviorSubject([]);
+ readonly #currentUser = getCurrentUser();
constructor(
readonly userService: UserService,
@@ -138,7 +138,7 @@ export class EditDossierTeamComponent implements EditDossierSectionInterface, On
}
setMembersSelectOptions(value = this.searchQuery): void {
- const possibleMembers = this.userService.all.filter(user => user.has(ROLES.dossiers.read) || user.isUser || user.isManager);
+ const possibleMembers = this.userService.all.filter(user => user.isUser || user.isManager);
this.membersSelectOptions = possibleMembers
.filter(user => this.userService.getName(user.id).toLowerCase().includes(value.toLowerCase()))
.filter(user => this.selectedOwnerId !== user.id)
diff --git a/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.html b/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.html
index f70356724..77d431388 100644
--- a/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.html
+++ b/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.html
@@ -19,7 +19,7 @@
();
@Input() memberIds: List;
@Input() perLine: number;
diff --git a/apps/red-ui/src/app/services/permissions.service.ts b/apps/red-ui/src/app/services/permissions.service.ts
index a6d4e0a92..103d8a0c1 100644
--- a/apps/red-ui/src/app/services/permissions.service.ts
+++ b/apps/red-ui/src/app/services/permissions.service.ts
@@ -23,14 +23,19 @@ export class PermissionsService {
return this.isAdmin() || this.isManager();
}
+ canPerformDossierStatesActions() {
+ return this.isAdmin() && this._iqserPermissionsService.has(ROLES.states.write);
+ }
+
canEditEntities(): boolean {
- return this._iqserPermissionsService.has(ROLES.dictionaryTypes.write);
+ return this._iqserPermissionsService.has(ROLES.dictionaryTypes.write) && this.isAdmin();
}
canDeleteEntities(entity: Dictionary | Dictionary[]): boolean {
const entities = entity instanceof Dictionary ? [entity] : entity;
return (
entities.length &&
+ this.isAdmin() &&
this._iqserPermissionsService.has(ROLES.dictionaryTypes.delete) &&
entities.reduce((acc, _entity) => this._canDeleteEntity(_entity) && acc, true)
);
@@ -239,7 +244,10 @@ export class PermissionsService {
}
canSoftDeleteDossier(dossier: IDossier): boolean {
- return this._iqserPermissionsService.has(ROLES.dossiers.delete) && (this.isOwner(dossier) || this.isDossierMember(dossier));
+ return (
+ this._iqserPermissionsService.has(ROLES.dossiers.delete) &&
+ (this.isOwner(dossier) || (this.isManager() && this.isDossierMember(dossier)))
+ );
}
canHardDeleteDossier(dossier: IDossier): boolean {
@@ -251,7 +259,7 @@ export class PermissionsService {
}
canCreateDossier(dossierTemplate: DossierTemplate | DashboardStats): boolean {
- return this._iqserPermissionsService.has(ROLES.dossiers.write) && dossierTemplate.isActive;
+ return this._iqserPermissionsService.has(ROLES.dossiers.write) && this.isManager() && dossierTemplate.isActive;
}
canArchiveDossier(dossier: Dossier): boolean {
@@ -264,7 +272,7 @@ export class PermissionsService {
}
canEditDossier(dossier: Dossier): boolean {
- return this._iqserPermissionsService.has(ROLES.dossiers.edit) && !!dossier?.ownerId;
+ return this._iqserPermissionsService.has(ROLES.dossiers.edit) && this.isManager() && !!dossier?.ownerId;
}
canEditDossierDictionary(dossier: Dossier): boolean {
@@ -276,7 +284,7 @@ export class PermissionsService {
}
canEditTeamMembers(): boolean {
- return this._iqserPermissionsService.has(ROLES.dossiers.edit);
+ return this._iqserPermissionsService.has(ROLES.dossiers.edit) && this.isManager();
}
isAdmin(): boolean {
diff --git a/apps/red-ui/src/app/users/roles.ts b/apps/red-ui/src/app/users/roles.ts
index 49245a57d..26bb7c4bc 100644
--- a/apps/red-ui/src/app/users/roles.ts
+++ b/apps/red-ui/src/app/users/roles.ts
@@ -2,15 +2,15 @@ export const ROLES = {
RED_CREATE_TENANT: 'red-create-tenant',
RED_DEPLOYMENT_INFO: 'red-deployment-info',
RED_GET_TENANTS: 'red-get-tenants',
- RED_MANAGE_USER_PREFERENCES: 'red-manage-user-preferences',
RED_PROCESS_DOWNLOAD: 'red-process-download',
RED_PROCESS_MANUAL_REDACTION_REQUEST: 'red-process-manual-redaction-request',
RED_READ_RULES: 'red-read-rules',
RED_READ_VERSIONS: 'red-read-versions',
RED_REINDEX: 'red-reindex',
RED_ROTATE_PAGE: 'red-rotate-page',
- RED_UPDATE_MY_PROFILE: 'red-update-my-profile',
RED_WRITE_RULES: 'red-write-rules',
+ managePreferences: 'red-manage-user-preferences',
+ updateMyProfile: 'red-update-my-profile',
getRss: 'red-get-rss',
excludeIncludeFile: 'red-exclude-include-file',
excludeIncludePages: 'red-exclude-include-pages',
diff --git a/apps/red-ui/src/app/users/user.service.ts b/apps/red-ui/src/app/users/user.service.ts
index 1970b7f49..6f1bf439b 100644
--- a/apps/red-ui/src/app/users/user.service.ts
+++ b/apps/red-ui/src/app/users/user.service.ts
@@ -17,7 +17,7 @@ export class UserService extends IqserUserService {
this._permissionsService.add({
[ROLES.any]: (permission, all) => {
console.log('all roles: ', Object.keys(all));
- return Object.keys(all).some(key => key.startsWith('red-'));
+ return Object.keys(all).some(key => key.startsWith('red-') || key.startsWith('RED_'));
},
});