From 130e4a35e44c99fe0c6f6da95d21b76bcf8db1fc Mon Sep 17 00:00:00 2001 From: Martin Stockhammer Date: Fri, 18 Dec 2020 21:29:24 +0100 Subject: [PATCH] Adding role information --- .../main/archiva-web/src/app/model/role.ts | 6 + .../modules/security/role-routing.module.ts | 1 + .../manage-roles-edit.component.html | 144 +++++++++++++++++- .../manage-roles-edit.component.ts | 142 ++++++++++++++++- .../manage-roles-list.component.html | 3 + .../manage-roles-list.component.ts | 11 +- .../manage-roles/manage-roles.component.ts | 1 - .../manage-users-edit.component.html | 2 +- .../app/modules/shared/edit-base.component.ts | 133 ++++++++++++++++ .../src/app/modules/shared/shared.module.ts | 4 +- .../modules/shared/sorted-table-component.ts | 11 ++ .../src/app/services/role.service.ts | 9 +- .../main/archiva-web/src/assets/i18n/en.json | 20 ++- 13 files changed, 465 insertions(+), 22 deletions(-) create mode 100644 archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/edit-base.component.ts create mode 100644 archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/sorted-table-component.ts diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/role.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/role.ts index 819d0f1e5..ac0b46919 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/role.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/role.ts @@ -16,6 +16,8 @@ * under the License. */ +import {Permission} from "@app/model/permission"; + export class Role { id: string name: string @@ -29,7 +31,11 @@ export class Role { model_id:string resource:string + child_role_ids: Array + parent_role_ids: Array children: Array + parents: Array + permissions: Array // Web Internal attributes enabled: boolean = true diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/role-routing.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/role-routing.module.ts index 6452e6569..1be49d717 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/role-routing.module.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/role-routing.module.ts @@ -35,6 +35,7 @@ const routes: Routes = [ children: [ {path: 'list', component: ManageRolesListComponent}, {path: 'edit/:roleid', component: ManageRolesEditComponent}, + {path: 'edit', component: ManageRolesEditComponent}, {path: '', redirectTo: 'list', pathMatch: 'full'} ] } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.html index 292d9d59b..d92d21c4e 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.html +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.html @@ -16,4 +16,146 @@ ~ under the License. --> -

manage-roles-edit works!

+
+
+
{{'form.edit' |translate}} 
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+ + +
+
+ + +
+
+
+
+
+ + + +
+ + + +
+
+ + + + + +
+ + +
+ + + +
+
+ + + + + +
+ + + +
+ + + + +
+
+ + + + + + + + + + + + + + + + + + +
{{'permissions.attributes.permission'|translate}}{{'permissions.attributes.operation'|translate}}{{'permissions.attributes.resource'|translate}}
{{perm.name}}{{perm.operation.name}}{{perm.resource.identifier}}
+
+
+
+ + + +
+ + + +
+
+ +

There are the users

+
+
+ +
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.ts index 4a642cb3c..cbbd5d5ab 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.ts @@ -16,18 +16,144 @@ * under the License. */ -import { Component, OnInit } from '@angular/core'; +import {Component, EventEmitter, OnInit, Output} from '@angular/core'; +import {ActivatedRoute} from "@angular/router"; +import {FormBuilder, Validators} from "@angular/forms"; +import {RoleService} from "@app/services/role.service"; +import {catchError, filter, map, switchMap, tap} from "rxjs/operators"; +import {Role} from '@app/model/role'; +import {ErrorResult} from "@app/model/error-result"; +import {EditBaseComponent} from "@app/modules/shared/edit-base.component"; +import {forkJoin, iif, Observable, of, pipe, zip} from 'rxjs'; @Component({ - selector: 'app-manage-roles-edit', - templateUrl: './manage-roles-edit.component.html', - styleUrls: ['./manage-roles-edit.component.scss'] + selector: 'app-manage-roles-edit', + templateUrl: './manage-roles-edit.component.html', + styleUrls: ['./manage-roles-edit.component.scss'] }) -export class ManageRolesEditComponent implements OnInit { +export class ManageRolesEditComponent extends EditBaseComponent implements OnInit { - constructor() { } + parentsOpened: boolean - ngOnInit(): void { - } + editRole: Role; + editProperties = ['id', 'name', 'description', 'template_instance', 'resource', 'assignable']; + originRole; + roleCache: Map = new Map(); + + + @Output() + roleIdEvent: EventEmitter = new EventEmitter(true); + + constructor(private route: ActivatedRoute, private roleService: RoleService, public fb: FormBuilder) { + super(fb); + super.init(fb.group({ + id: ['', [Validators.required]], + name: ['', Validators.required], + description: [''], + resource: [''], + template_instance: [''], + assignable: [''] + }, {})); + } + + createEntity(): Role { + return new Role(); + } + + ngOnInit(): void { + this.route.params.pipe( + map(params => params.roleid), + filter(roleid => roleid != null), + tap(roleid => { + this.roleIdEvent.emit(roleid) + }), + switchMap((roleid: string) => this.roleService.getRole(roleid)), + switchMap((role: Role) => zip(of(role), + this.retrieveChildren(role), + this.retrieveParents(role))), + map((ra: [Role, Role[], Role[]]) => this.combine(ra)) + ).subscribe(role => { + this.editRole = role; + this.originRole = role; + this.copyToForm(this.editProperties, this.editRole); + }, error => { + this.editRole = new Role(); + }); + } + + /** + * Array of [role, children[], parents[]] + */ + combine(roleArray: [Role, Role[], Role[]]): Role { + roleArray[0].children = roleArray[1]; + roleArray[0].parents = roleArray[2]; + return roleArray[0]; + } + + private createRole(id: string): Role { + let role = new Role(); + role.id = id; + role.name='' + return role; + } + + getCachedRole(id : string) : Observable { + return of(id).pipe( + switchMap(( myId : string ) => { + if (this.roleCache.has(myId)) { + return of(this.roleCache.get(myId)); + } else { + return this.roleService.getRole(myId).pipe(tap(role => { + this.roleCache.set(role.id, role); + }),catchError(() => of(this.createRole(id)))); + } + })); + } + + retrieveChildren(role: Role): Observable { + // ForkJoin does not emit, if one of the observables is failing to emit a object + // -> we use catchError() + let children: Array> = [] + for (let child_id of role.child_role_ids) { + children.push(this.getCachedRole(child_id)); + } + if (children.length>0) { + return forkJoin(children); + } else { + return of([]); + } + } + + retrieveParents(role: Role): Observable { + let parents: Array> = [] + for (let parent_id of role.parent_role_ids) { + parents.push(this.getCachedRole(parent_id)); + } + if (parents.length>0) { + return forkJoin(parents); + } else { + return of([]); + } + } + + onSubmit() { + let role = this.copyFromForm(this.editProperties); + this.roleService.updateRole(role).pipe( + catchError((err: ErrorResult) => { + this.error = true; + this.success = false; + this.errorResult = err; + return []; + }) + ).subscribe(roleInfo => { + this.error = false; + this.success = true; + this.errorResult = null; + this.result = roleInfo; + this.editMode = false; + }); + + } } + diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-list/manage-roles-list.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-list/manage-roles-list.component.html index 8b21e6c23..7d904e7c1 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-list/manage-roles-list.component.html +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-list/manage-roles-list.component.html @@ -26,6 +26,7 @@ + Action @@ -36,6 +37,8 @@ {{role.id}} {{role.name}} {{role.description}} + {{role.resource}} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-list/manage-roles-list.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-list/manage-roles-list.component.ts index e3998d779..b49f64d98 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-list/manage-roles-list.component.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-list/manage-roles-list.component.ts @@ -25,21 +25,20 @@ import {Observable} from "rxjs"; import {PagedResult} from "@app/model/paged-result"; import {UserInfo} from "@app/model/user-info"; import {RoleService} from "@app/services/role.service"; +import {SortedTableComponent} from "@app/modules/shared/sorted-table-component"; @Component({ selector: 'app-manage-roles-list', templateUrl: './manage-roles-list.component.html', styleUrls: ['./manage-roles-list.component.scss'] }) -export class ManageRolesListComponent implements OnInit { +export class ManageRolesListComponent extends SortedTableComponent implements OnInit { - service: EntityService - - constructor(private translator: TranslateService, private roleService : RoleService) { - this.service = function (searchTerm: string, offset: number, limit: number, orderBy: string[], order: string) : Observable> { + constructor(translator: TranslateService, roleService : RoleService) { + super(translator, function (searchTerm: string, offset: number, limit: number, orderBy: string[], order: string): Observable> { console.log("Retrieving data " + searchTerm + "," + offset + "," + limit + "," + orderBy + "," + order); return roleService.query(searchTerm, offset, limit, orderBy, order); - } + }); } ngOnInit(): void { diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles/manage-roles.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles/manage-roles.component.ts index f2a67ddef..74af9c2d0 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles/manage-roles.component.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles/manage-roles.component.ts @@ -39,7 +39,6 @@ export class ManageRolesComponent implements OnInit { // console.log("Activating "+componentReference+" - "+JSON.stringify(componentReference,getCircularReplacer())) if (componentReference.roleIdEvent!=null) { let componentEmit : Observable = componentReference.roleIdEvent.pipe( - tap(userid=>console.log("Event "+componentReference.class+" "+userid)), map((userid: string) => this.getSubPath(userid))); if (this.roleId$!=null) { this.roleId$ = merge(this.roleId$, componentEmit) diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/users/manage-users-edit/manage-users-edit.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/users/manage-users-edit/manage-users-edit.component.html index ba8becf38..8f12e3bf7 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/users/manage-users-edit/manage-users-edit.component.html +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/users/manage-users-edit/manage-users-edit.component.html @@ -18,7 +18,7 @@
-
Edit
+
{{'form.edit' |translate}}
{ + + editProperties = ['id']; + success: boolean = false; + error: boolean = false; + errorResult: ErrorResult; + result: T; + formInitialValues; + public userForm : FormGroup; + public editMode: boolean; + + constructor(public fb: FormBuilder) { + + } + + init(userForm: FormGroup) : void { + this.userForm=userForm; + this.formInitialValues = userForm.value; + } + + abstract createEntity() : T; + abstract onSubmit(); + + public copyFromForm(properties: string[]): T { + let entity: any = this.createEntity(); + for (let prop of properties) { + entity[prop] = this.userForm.get(prop).value; + } + return entity; + } + + public copyToForm(properties: string[], user: T): void { + let propMap = {}; + for (let prop of properties) { + let propValue = user[prop] == null ? '' : user[prop]; + propMap[prop] = propValue; + } + this.userForm.patchValue(propMap); + } + + + valid(field: string): string[] { + if (this.editMode) { + let classArr = this.isValid(field); + return classArr.concat('form-control') + } else { + return ['form-control-plaintext']; + } + } + + isValid(field: string): string[] { + let formField = this.userForm.get(field); + if (formField.dirty || formField.touched) { + if (formField.valid) { + return ['is-valid'] + } else { + return ['is-invalid'] + } + } else { + return [''] + } + } + + forbiddenNameValidator(nameRe: RegExp): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } | null => { + const forbidden = nameRe.test(control.value); + return forbidden ? {forbiddenName: {value: control.value}} : null; + }; + } + + getAllErrors(formGroup: FormGroup, errors: string[] = []) : string[] { + Object.keys(formGroup.controls).forEach(field => { + const control = formGroup.get(field); + if (control instanceof FormControl && control.errors != null) { + let keys = Object.keys(control.errors).map(errorKey=>field+'.'+errorKey); + errors = errors.concat(keys); + } else if (control instanceof FormGroup) { + errors = errors.concat(this.getAllErrors(control)); + } + }); + return errors; + } + + getAttributeErrors(control:string):string[] { + return Object.keys(this.userForm.get(control).errors); + } +} + +export function whitespaceValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const hasWhitespace = /\s/g.test(control.value); + return hasWhitespace ? {containsWhitespace: {value: control.value}} : null; + }; +} +export function MustMatch(controlName: string, matchingControlName: string) : ValidatorFn { + return (formGroup: FormGroup): ValidationErrors | null => { + const control = formGroup.controls[controlName]; + const matchingControl = formGroup.controls[matchingControlName]; + + if (matchingControl.errors && !matchingControl.errors.mustMatch) { + // return if another validator has already found an error on the matchingControl + return; + } + + // set error on matchingControl if validation fails + if (control.value !== matchingControl.value) { + matchingControl.setErrors({mustMatch: true}); + } else { + matchingControl.setErrors(null); + } + } +} \ No newline at end of file diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/shared.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/shared.module.ts index 1030390d8..27978a345 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/shared.module.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/shared.module.ts @@ -21,12 +21,13 @@ import {CommonModule} from '@angular/common'; import {PaginatedEntitiesComponent} from "./paginated-entities/paginated-entities.component"; import {SortedTableHeaderComponent} from "./sorted-table-header/sorted-table-header.component"; import {SortedTableHeaderRowComponent} from "./sorted-table-header-row/sorted-table-header-row.component"; -import {NgbPaginationModule, NgbTooltipModule} from "@ng-bootstrap/ng-bootstrap"; +import {NgbAccordionModule, NgbPaginationModule, NgbTooltipModule} from "@ng-bootstrap/ng-bootstrap"; import {TranslateCompiler, TranslateLoader, TranslateModule} from "@ngx-translate/core"; import {TranslateMessageFormatCompiler} from "ngx-translate-messageformat-compiler"; import {HttpClient} from "@angular/common/http"; import {TranslateHttpLoader} from "@ngx-translate/http-loader"; import {RouterModule} from "@angular/router"; +import {SortedTableComponent} from "@app/modules/shared/sorted-table-component"; @NgModule({ @@ -41,6 +42,7 @@ import {RouterModule} from "@angular/router"; TranslateModule, NgbPaginationModule, NgbTooltipModule, + NgbAccordionModule, PaginatedEntitiesComponent, SortedTableHeaderComponent, SortedTableHeaderRowComponent diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/sorted-table-component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/sorted-table-component.ts new file mode 100644 index 000000000..27423faf1 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/sorted-table-component.ts @@ -0,0 +1,11 @@ +import {EntityService} from "@app/model/entity-service"; +import {TranslateService} from "@ngx-translate/core"; + +export class SortedTableComponent { + + sortField = ["id"]; + sortOrder = "asc"; + + constructor(public translator : TranslateService, public service: EntityService) { + } +} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/role.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/role.service.ts index 79bee947f..3de9919dc 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/role.service.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/role.service.ts @@ -45,7 +45,6 @@ export class RoleService { } public query(searchTerm: string, offset: number = 0, limit: number = 10, orderBy: string[] = ['id'], order: string = 'asc'): Observable> { - console.log("getRoleList " + searchTerm + "," + offset + "," + limit + "," + orderBy + "," + order); if (searchTerm == null) { searchTerm = "" } @@ -61,4 +60,12 @@ export class RoleService { }); } + public getRole(roleId:string) : Observable { + return this.rest.executeRestCall("get", "redback", "roles/" + roleId, null); + } + + public updateRole(role:Role) : Observable { + return this.rest.executeRestCall("put", "redback", "roles/" + role.id, role); + } + } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json index 6c39c16b9..7bdf9b85c 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json @@ -132,16 +132,29 @@ "head": "Roles List" }, "edit": { - "head": "Edit/View Role" + "head": "Edit/View Role", + "parents": "Parents", + "children": "Children", + "permissions": "Permissions", + "users": "Users" }, "attributes": { "id": "Identifier", "name": "Name", "description": "Description", - "template_instance": "Template Instance" + "template_instance": "Template Instance", + "resource": "Repository", + "assignable": "Assignable" } }, + "permissions": { + "attributes": { + "permission": "Permission", + "operation": "Operation", + "resource": "Resource" + } + }, "search": { "button": "Search", "label": "Enter your search term", @@ -158,7 +171,8 @@ "yes": "Yes", "no": "No", "save": "Save Changes" - } + }, + "edit": "Edit" }, "password": { "violations" : { -- 2.39.5