Browse Source

Refactor access management: reorder parameters, enhance validation, and improve dialog handling

pull/5566/head
Germán Martín 1 month ago
parent
commit
ae6f5b6255
  1. 4
      apps/api/src/app/access/access.controller.ts
  2. 12
      apps/api/src/app/access/access.service.ts
  3. 6
      apps/api/src/app/access/update-access.dto.ts
  4. 1
      apps/client/src/app/components/access-table/access-table.component.html
  5. 39
      apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts
  6. 30
      apps/client/src/app/components/user-account-access/user-account-access.component.ts
  7. 2
      libs/common/src/lib/permissions.ts

4
apps/api/src/app/access/access.controller.ts

@ -129,8 +129,8 @@ export class AccessController {
@Put(':id') @Put(':id')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async updateAccess( public async updateAccess(
@Param('id') id: string, @Body() data: UpdateAccessDto,
@Body() data: UpdateAccessDto @Param('id') id: string
): Promise<AccessModel> { ): Promise<AccessModel> {
if ( if (
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') &&

12
apps/api/src/app/access/access.service.ts

@ -20,16 +20,14 @@ export class AccessService {
} }
public async accesses(params: { public async accesses(params: {
cursor?: Prisma.AccessWhereUniqueInput;
include?: Prisma.AccessInclude; include?: Prisma.AccessInclude;
orderBy?: Prisma.Enumerable<Prisma.AccessOrderByWithRelationInput>;
skip?: number; skip?: number;
take?: number; take?: number;
cursor?: Prisma.AccessWhereUniqueInput;
where?: Prisma.AccessWhereInput; where?: Prisma.AccessWhereInput;
orderBy?:
| Prisma.AccessOrderByWithRelationInput
| Prisma.AccessOrderByWithRelationInput[];
}): Promise<AccessWithGranteeUser[]> { }): Promise<AccessWithGranteeUser[]> {
const { include, skip, take, cursor, where, orderBy } = params; const { cursor, include, orderBy, skip, take, where } = params;
return this.prismaService.access.findMany({ return this.prismaService.access.findMany({
cursor, cursor,
@ -63,8 +61,8 @@ export class AccessService {
where: Prisma.AccessWhereUniqueInput; where: Prisma.AccessWhereUniqueInput;
}): Promise<Access> { }): Promise<Access> {
return this.prismaService.access.update({ return this.prismaService.access.update({
where, data,
data where
}); });
} }
} }

6
apps/api/src/app/access/update-access.dto.ts

@ -2,9 +2,6 @@ import { AccessPermission } from '@prisma/client';
import { IsEnum, IsOptional, IsString, IsUUID } from 'class-validator'; import { IsEnum, IsOptional, IsString, IsUUID } from 'class-validator';
export class UpdateAccessDto { export class UpdateAccessDto {
@IsString()
id: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
alias?: string; alias?: string;
@ -13,6 +10,9 @@ export class UpdateAccessDto {
@IsUUID() @IsUUID()
granteeUserId?: string; granteeUserId?: string;
@IsString()
id: string;
@IsEnum(AccessPermission, { each: true }) @IsEnum(AccessPermission, { each: true })
@IsOptional() @IsOptional()
permissions?: AccessPermission[]; permissions?: AccessPermission[];

1
apps/client/src/app/components/access-table/access-table.component.html

@ -72,6 +72,7 @@
<span i18n>Edit</span> <span i18n>Edit</span>
</span> </span>
</button> </button>
<hr class="my-0" />
} }
@if (element.type === 'PUBLIC') { @if (element.type === 'PUBLIC') {
<button mat-menu-item (click)="onCopyUrlToClipboard(element.id)"> <button mat-menu-item (click)="onCopyUrlToClipboard(element.id)">

39
apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts

@ -68,22 +68,18 @@ export class GfCreateOrUpdateAccessDialogComponent
this.mode = this.data.access?.id ? 'update' : 'create'; this.mode = this.data.access?.id ? 'update' : 'create';
} }
public onCancel() {
this.dialogRef.close();
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
public ngOnInit() { public ngOnInit() {
const isPublic = this.data.access.type === 'PUBLIC';
this.accessForm = this.formBuilder.group({ this.accessForm = this.formBuilder.group({
alias: [this.data.access.alias], alias: [this.data.access.alias],
granteeUserId: [this.data.access.grantee, Validators.required], granteeUserId: [
this.data.access.grantee,
isPublic ? null : Validators.required
],
permissions: [this.data.access.permissions[0], Validators.required], permissions: [this.data.access.permissions[0], Validators.required],
type: [ type: [
{ value: this.data.access.type, disabled: this.mode === 'update' }, { disabled: this.mode === 'update', value: this.data.access.type },
Validators.required Validators.required
] ]
}); });
@ -104,14 +100,10 @@ export class GfCreateOrUpdateAccessDialogComponent
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
}
// Initial validation setup based on current type public onCancel() {
if (this.accessForm.get('type').value === 'PUBLIC') { this.dialogRef.close();
const granteeUserIdControl = this.accessForm.get('granteeUserId');
granteeUserIdControl.clearValidators();
granteeUserIdControl.setValue(null);
granteeUserIdControl.updateValueAndValidity();
}
} }
public async onSubmit() { public async onSubmit() {
@ -122,6 +114,11 @@ export class GfCreateOrUpdateAccessDialogComponent
} }
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
private async createAccess() { private async createAccess() {
const access: CreateAccessDto = { const access: CreateAccessDto = {
alias: this.accessForm.get('alias').value, alias: this.accessForm.get('alias').value,
@ -160,9 +157,9 @@ export class GfCreateOrUpdateAccessDialogComponent
private async updateAccess() { private async updateAccess() {
const access: UpdateAccessDto = { const access: UpdateAccessDto = {
id: this.data.access.id,
alias: this.accessForm.get('alias').value, alias: this.accessForm.get('alias').value,
granteeUserId: this.accessForm.get('granteeUserId').value, granteeUserId: this.accessForm.get('granteeUserId').value,
id: this.data.access.id,
permissions: [this.accessForm.get('permissions').value] permissions: [this.accessForm.get('permissions').value]
}; };
@ -176,8 +173,8 @@ export class GfCreateOrUpdateAccessDialogComponent
this.dataService this.dataService
.putAccess(access) .putAccess(access)
.pipe( .pipe(
catchError((error) => { catchError(({ status }) => {
if (error.status === StatusCodes.BAD_REQUEST) { if (status.status === StatusCodes.BAD_REQUEST) {
this.notificationService.alert({ this.notificationService.alert({
title: $localize`Oops! Could not update access.` title: $localize`Oops! Could not update access.`
}); });

30
apps/client/src/app/components/user-account-access/user-account-access.component.ts

@ -115,8 +115,8 @@ export class GfUserAccountAccessComponent implements OnDestroy, OnInit {
.subscribe((params) => { .subscribe((params) => {
if (params['createDialog']) { if (params['createDialog']) {
this.openCreateAccessDialog(); this.openCreateAccessDialog();
} else if (params['editDialog']) { } else if (params['editDialog'] && params['accessId']) {
this.openUpdateAccessDialog(params['editDialog']); this.openUpdateAccessDialog(params['accessId']);
} }
}); });
@ -140,10 +140,6 @@ export class GfUserAccountAccessComponent implements OnDestroy, OnInit {
}); });
} }
public onUpdateAccess(aId: string) {
this.openUpdateAccessDialog(aId);
}
public onGenerateAccessToken() { public onGenerateAccessToken() {
this.notificationService.confirm({ this.notificationService.confirm({
confirmFn: () => { confirmFn: () => {
@ -179,6 +175,12 @@ export class GfUserAccountAccessComponent implements OnDestroy, OnInit {
}); });
} }
public onUpdateAccess(aId: string) {
this.router.navigate([], {
queryParams: { accessId: aId, editDialog: true }
});
}
public ngOnDestroy() { public ngOnDestroy() {
this.unsubscribeSubject.next(); this.unsubscribeSubject.next();
this.unsubscribeSubject.complete(); this.unsubscribeSubject.complete();
@ -208,9 +210,9 @@ export class GfUserAccountAccessComponent implements OnDestroy, OnInit {
private openUpdateAccessDialog(accessId: string) { private openUpdateAccessDialog(accessId: string) {
// Find the access details in the already loaded data // Find the access details in the already loaded data
const accessDetails = this.accessesGive.find( const accessDetails = this.accessesGive.find(({ id }) => {
(access) => access.id === accessId return id === accessId;
); });
if (!accessDetails) { if (!accessDetails) {
this.notificationService.alert({ this.notificationService.alert({
@ -222,12 +224,12 @@ export class GfUserAccountAccessComponent implements OnDestroy, OnInit {
const dialogRef = this.dialog.open(GfCreateOrUpdateAccessDialogComponent, { const dialogRef = this.dialog.open(GfCreateOrUpdateAccessDialogComponent, {
data: { data: {
access: { access: {
id: accessDetails.id,
alias: accessDetails.alias, alias: accessDetails.alias,
permissions: accessDetails.permissions, id: accessDetails.id,
type: accessDetails.type,
grantee: grantee:
accessDetails.grantee === 'Public' ? null : accessDetails.grantee accessDetails.grantee === 'Public' ? null : accessDetails.grantee,
permissions: accessDetails.permissions,
type: accessDetails.type
} }
}, },
height: this.deviceType === 'mobile' ? '98vh' : undefined, height: this.deviceType === 'mobile' ? '98vh' : undefined,
@ -238,6 +240,8 @@ export class GfUserAccountAccessComponent implements OnDestroy, OnInit {
if (result) { if (result) {
this.update(); this.update();
} }
this.router.navigate(['.'], { relativeTo: this.route });
}); });
} }

2
libs/common/src/lib/permissions.ts

@ -184,7 +184,7 @@ export function hasReadRestrictedAccessPermission({
impersonationId: string; impersonationId: string;
user: UserWithSettings; user: UserWithSettings;
}) { }) {
if (!impersonationId || !user) { if (!impersonationId) {
return false; return false;
} }

Loading…
Cancel
Save