* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
import { Application } from './application';
describe('Application', () => {
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
export class Application {
}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
import { RoleTemplate } from './role-template';
describe('RoleTemplate', () => {
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
export class RoleTemplate {
id:string
name:string
// Web Internal attributes
enabled: boolean = true
level:number = -1
+ root_path: Array<string>
+ assigned_origin: boolean;
}
~ with the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
- ~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
[attr.disabled]="userForm.valid?null:true">{{'users.add.submit'|translate}}</button>
</div>
<div *ngIf="success" class="alert alert-success" role="alert">
- User <a [routerLink]="['/user','users','edit',result?.user_id]">{{result?.user_id}}</a> was added to the list.
+ User <a [routerLink]="['/security','users','edit',result?.user_id]">{{result?.user_id}}</a> was added to the list.
</div>
<div *ngIf="error" class="alert alert-danger" role="alert" >
<h4 class="alert-heading">{{'users.add.errortitle'|translate}}</h4>
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
- *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
- *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
- *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* under the License.
*/
-import {Component, OnInit} from '@angular/core';
+import {Component, EventEmitter, OnInit, Output} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {UserService} from "../../../../services/user.service";
import {FormBuilder, FormControl} from "@angular/forms";
-import {catchError, map, switchMap, tap} from 'rxjs/operators';
+import {catchError, filter, map, switchMap, tap} from 'rxjs/operators';
import {ManageUsersBaseComponent} from "../manage-users-base.component";
import {ErrorResult} from "../../../../model/error-result";
editMode: boolean;
minUserIdSize = 0;
+ @Output()
+ userIdEvent: EventEmitter<string> = new EventEmitter<string>(true);
+
constructor(private route: ActivatedRoute, public userService: UserService, public fb: FormBuilder) {
super(userService, fb);
this.editMode=false;
this.editMode=true;
}
})
+
+ }
+
+ ngOnInit(): void {
this.editUser = this.route.params.pipe(
- map(params => params.userid), switchMap(userid => userService.getUser(userid))).subscribe(user => {
+ map(params => params.userid),
+ filter(userid=>userid!=null),
+ tap(userid=>{
+ this.userIdEvent.emit(userid)
+ }),
+ switchMap(userid => this.userService.getUser(userid))).subscribe(user => {
this.editUser = user;
this.originUser = user;
this.copyToForm(this.editProperties, this.editUser);
});
- }
-
- ngOnInit(): void {
// This resets the validators of the base class
this.userForm.get('user_id').clearValidators();
this.userForm.get('user_id').clearAsyncValidators();
~ with the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
- ~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
- *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
- *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
- *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-<h3>Roles</h3>
-<table class="table">
- <thead class="thead-light">
- <tr class="d-flex">
- <th class="col-3">Role</th>
- <th class="col-2">Scope</th>
- <th class="col-1">Assign</th>
- </tr>
- </thead>
- <tbody>
- <tr *ngFor="let baseRole of baseRoles" class="d-flex">
- <td class="col-3" [innerHTML]="getRoleContent(baseRole)"></td>
- <td class="col-2">
- {{baseRole.application_id}}
- </td>
- <td class="col-1">
- <div class="form-check form-check-inline"><input class="form-check-input" type="checkbox"
- [attr.disabled]="baseRole.enabled?null:true"
- [attr.id]="baseRole.id"
- [(ngModel)]="baseRole.assigned"
- (change)="changeBaseAssignment(baseRole, $event)"
- >
+
+
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ -->
+
+<div class="row">
+
+ <div class="row col-md-6">
+ <h4 class="col-md-2 mt-3">{{'users.roles.base_roles'|translate}} </h4>
+ <h4 class="col-md-2 offset-md-4 mt-3"><span class="badge badge-primary">{{userid}}</span></h4>
+ </div>
+ <ng-container *ngIf="roles$|async as myRoles">
+ <table class="table col-md-12">
+ <thead class="thead-light">
+ <tr class="d-flex">
+ <th class="col-3">{{'users.roles.table.role'|translate}}</th>
+ <th class="col-2">{{'users.roles.table.scope'|translate}}</th>
+ <th class="col-1">{{'users.roles.table.assign'|translate}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let baseRoleItem of myRoles.baseRoles|keyvalue" class="d-flex">
+ <td class="col-3" [innerHTML]="getRoleContent(baseRoleItem.value)"></td>
+ <td class="col-2">
+ {{baseRoleItem.value.application_id}}
+ </td>
+ <td class="col-1">
+ <div class="form-check form-check-inline"><input class="form-check-input" type="checkbox"
+ [attr.disabled]="baseRoleItem.value.enabled?null:true"
+ [attr.id]="baseRoleItem.key"
+ [(ngModel)]="baseRoleItem.value.assigned"
+ (change)="changeBaseAssignment(baseRoleItem.value, $event)"
+ >
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+
+ <h4>{{'users.roles.template_roles'|translate}}</h4>
+ <table class="table">
+ <thead class="thead-light">
+ <tr class="d-flex">
+ <th scope="row" class="col-1">{{'users.roles.table.repository'|translate}}</th>
+ <th scope="col" class="col-1"
+ *ngFor="let templateRole of templateRoles$ | async">{{templateRole.name}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr class="d-flex" *ngFor="let res of myRoles.templateRoleInstances | keyvalue">
+ <td class="table-secondary col-1">{{res.key}}</td>
+ <td class="col-1 text-center" *ngFor="let templateRole of templateRoles$ | async">
+ <div class="form-check form-check-inline"
+ *ngIf="getInstanceContent(templateRole, res.value) as role">
+ <input class="form-check-input"
+ [attr.disabled]="role.enabled?null:true"
+ type="checkbox" [attr.id]="role.id"
+ [(ngModel)]="role.assigned" (ngModelChange)="changeTemplateAssignment(role, $event)"/>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+
+ </table>
+
+ <div class="form-group col-md-12 mt-3 ">
+ <button class="btn btn-primary" type="submit"
+ (click)="saveAssignments()">{{'form.button.save'|translate}}</button>
+ </div>
+
+ <ng-container *ngIf="saved">
+ <div *ngIf="success" class="alert alert-success" role="alert">
+ Roles have been assigned
</div>
- </td>
- </tr>
- </tbody>
-</table>
-
-
-<h3>Repository Roles</h3>
-<table class="table">
- <thead class="thead-light">
- <tr class="d-flex">
- <th scope="row" class="col-1">Repository</th>
- <th scope="col" class="col-1" *ngFor="let templateRole of templateRoles$ | async">{{templateRole.name}}</th>
- </tr>
- </thead>
- <tbody>
- <tr class="d-flex" *ngFor="let res of templateRoleInstances | keyvalue" >
- <td class="table-secondary col-1">{{res.key}}</td>
- <td class="col-1 text-center" *ngFor="let templateRole of templateRoles$ | async">
- <div class="form-check form-check-inline" *ngIf="getInstanceContent(templateRole, res.value) as role">
- <input class="form-check-input" type="checkbox" [attr.id]="role.id"
- [(ngModel)]="role.assigned" (ngModelChange)="changeInstAssignment(role, $event)"/>
+ <div *ngIf="!success" class="alert alert-danger" role="alert">
+ <h4 class="alert-heading">Errors</h4>
+ <ng-container *ngFor="let message of errors.error_messages; first as isFirst">
+ <hr *ngIf="!isFirst">
+ <p>{{message.message}}</p>
+ </ng-container>
</div>
- </td>
- </tr>
- </tbody>
+ </ng-container>
+
+ </ng-container>
-</table>
+</div>
\ No newline at end of file
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ManageUsersRolesComponent } from './manage-users-roles.component';
-import { Component, OnInit } from '@angular/core';
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {AfterViewInit, Component, EventEmitter, OnInit, Output} from '@angular/core';
import { Role } from '@app/model/role';
import {UserService} from "@app/services/user.service";
import {ActivatedRoute} from "@angular/router";
-import {filter, map, switchMap} from "rxjs/operators";
+import {catchError, filter, map, multicast, share, switchMap, tap} from "rxjs/operators";
import {RoleTree} from "@app/model/role-tree";
import {RoleService} from "@app/services/role.service";
import {RoleTemplate} from "@app/model/role-template";
-import {Observable} from "rxjs";
+import {Observable, of} from "rxjs";
import {Util} from "@app/modules/shared/shared.module";
+import { RoleResult } from './role-result';
+import {fromArray} from "rxjs/internal/observable/fromArray";
+import {ErrorResult} from "@app/model/error-result";
+import {HttpResponse} from "@angular/common/http";
@Component({
selector: 'app-manage-users-roles',
templateUrl: './manage-users-roles.component.html',
styleUrls: ['./manage-users-roles.component.scss']
})
-export class ManageUsersRolesComponent implements OnInit {
+export class ManageUsersRolesComponent implements OnInit, AfterViewInit {
- baseRoles : Array<Role>
+ roles$ : Observable<RoleResult>
+ currentRoles: RoleResult
guest: Role
registered: Role
- // Map of (resource, [roles])
- templateRoleInstances: Map<string, Array<Role>>
templateRoles$: Observable<RoleTemplate[]>;
+ userid: string;
+ success:boolean=true;
+ errors: ErrorResult[]=[];
+ saved:boolean=false;
- constructor(private route : ActivatedRoute, private userService : UserService, private roleService : RoleService) {
- this.route.params.pipe(
- map(params => params.userid), filter(userid => userid!=null), switchMap(userid => userService.userRoleTree(userid))).subscribe(roleTree => {
- this.parseRoleTree(roleTree);
- });
+ @Output()
+ userIdEvent: EventEmitter<string> = new EventEmitter<string>(true);
+ constructor(private route : ActivatedRoute, private userService : UserService, private roleService : RoleService) {
}
ngOnInit(): void {
+ this.roles$ = this.route.params.pipe(
+ map(params => params.userid), filter(userid => userid != null),
+ tap(userid => this.userid = userid),
+ tap(userid=>{
+ this.userIdEvent.emit(userid)
+ }),
+ switchMap(userid => {
+ return this.userService.userRoleTree(userid)
+ }),
+ map(roleTree=>this.parseRoleTree(roleTree)),
+ // This is to avoid multiple userService.userRoleTree() calls for template and base roles
+ share()
+ );
this.templateRoles$ = this.roleService.getTemplates();
}
- private parseRoleTree(roleTree:RoleTree): void {
- let roleTable = [];
- for(let rootRole of roleTree.root_roles) {
- roleTable = this.recurseRoleTree(rootRole, roleTable, 0);
+ private parseRoleTree(roleTree:RoleTree): RoleResult {
+ let roleResult = new RoleResult();
+ let rootRoles = roleTree.root_roles;
+ rootRoles.sort((a, b)=>{
+ if (b.id=='guest') {
+ return 1;
+ } else if (b.id=='registered-user') {
+ return 1;
+ } else {
+ return -1;
+ }
+ })
+ for (let rootRole of rootRoles) {
+ this.recurseTree(rootRole, roleResult, 0, null);
+ }
+ return roleResult;
+ }
+
+ private recurseTree(role:Role,roleResult:RoleResult, level:number, parent: Role) : void {
+ let newLevel=level;
+ if (parent!=null) {
+ if (role.root_path==null) {
+ role.root_path = (parent.root_path == null ? [] : parent.root_path.slice());
+ }
+ role.root_path.push(parent.id)
}
- this.baseRoles = roleTable;
- let templateMap : Map<string,Array<Role>> = new Map<string, Array<Role>>();
- for (let rootRole of roleTree.root_roles) {
- templateMap = this.recurseTemplates(rootRole, templateMap, 0);
+ role.assigned_origin = role.assigned;
+ if (role.template_instance) {
+ newLevel = this.parseTemplateRole(role,roleResult.templateRoleInstances,newLevel)
+ } else {
+ newLevel = this.parseBaseRole(role, roleResult.baseRoles, newLevel)
+ }
+ for(let childRole of role.children) {
+ let recurseChild = childRole;
+ if (childRole.template_instance) {
+ if (roleResult.templateRoleInstances.has(childRole.resource) && roleResult.templateRoleInstances.get(childRole.resource).has(childRole.model_id)) {
+ recurseChild = roleResult.templateRoleInstances.get(childRole.resource).get(childRole.model_id)
+ }
+ } else {
+ let existingBaseRole = roleResult.baseRoles.find(role => role.id == childRole.id)
+ if (existingBaseRole != null) {
+ recurseChild = existingBaseRole;
+ }
+ }
+ this.recurseTree(recurseChild, roleResult, newLevel, role);
}
- this.templateRoleInstances = templateMap;
}
- private recurseRoleTree(role:Role, roles : Array<Role>, level:number) : Array<Role> {
+ private parseBaseRole(role:Role, roles : Array<Role>, level:number) : number {
if (role.id=='guest') {
this.guest = role;
} else if (role.id=='registered-user') {
}
role.enabled=true;
let newLevel;
- if (!role.template_instance && role.assignable) {
+ if (role.assignable) {
role.level=level
roles.push(role);
newLevel = level+1;
} else {
newLevel = level;
}
- for(let childRole of role.children) {
- roles = this.recurseRoleTree(childRole, roles, newLevel);
- }
- return roles;
+ return newLevel;
}
- private recurseTemplates(role:Role, roles : Map<string, Array<Role>>, level:number) : Map<string, Array<Role>> {
+ private parseTemplateRole(role:Role, roles : Map<string, Map<string, Role>>, level:number) : number {
+ let newLevel=level;
role.enabled=true;
- if (role.template_instance && role.assignable) {
+ if (role.assignable) {
role.level=level
- let roleList = roles.get(role.resource)
- if (roleList==null) {
- roleList = []
+ let modelRoleMap = roles.get(role.resource)
+ if (modelRoleMap==null) {
+ modelRoleMap = new Map<string, Role>();
}
- roleList.push(role);
- roles.set(role.resource, roleList);
- }
- for(let childRole of role.children) {
- roles = this.recurseTemplates(childRole, roles, level+1);
+ modelRoleMap.set(role.model_id, role);
+ roles.set(role.resource, modelRoleMap);
+ newLevel = level + 1;
+ } else {
+ newLevel = level;
}
- return roles;
+ return newLevel;
}
getRoleContent(role:Role) : string {
changeBaseAssignment(role : Role, event) {
let cLevel=-1
- let assignStatus;
+ let assignStatus
+ // Guest is special and exclusive
if (role.id==this.guest.id) {
if (role.assigned) {
- for (let cRole of this.baseRoles) {
+ this.currentRoles.baseRoles.forEach((cRole:Role)=> {
if (cRole.id != this.guest.id) {
cRole.assigned = false;
cRole.enabled=true;
}
- }
+
+ })
role.enabled = false;
+ this.currentRoles.templateRoleInstances.forEach((value, key)=>{
+ value.forEach((templateInstance, modelId) => {
+ templateInstance.assigned = false;
+ templateInstance.enabled = true;
+
+ });
+ })
}
} else {
this.guest.enabled = true;
- for (let cRole of this.baseRoles) {
+ this.currentRoles.baseRoles.forEach((cRole)=> {
if (cRole.id == role.id) {
- console.log("Value: " + cRole.assigned);
cLevel = cRole.level;
assignStatus = cRole.assigned;
if (assignStatus) {
this.guest.assigned = false;
+ this.guest.enabled = true;
} else {
- if (!this.baseRoles.find(role=>role.assigned)) {
+ if (!this.isAnyAssigned()) {
this.guest.assigned=true;
+ this.guest.enabled = false;
}
}
} else {
- console.log("Level " + cLevel);
- if (cLevel >= 0 && cLevel < cRole.level) {
+ if (cLevel >= 0 && cLevel < cRole.level && cRole.root_path.find(pRoleId => pRoleId==role.id)) {
if (assignStatus) {
cRole.assigned = true;
cRole.enabled = false;
} else {
cRole.enabled = true;
+ cRole.assigned=cRole.assigned_origin
}
} else if (cLevel >= 0) {
- break;
+ return;
}
}
+ })
+ this.currentRoles.templateRoleInstances.forEach((value, key)=>{
+ value.forEach((templateInstance, modelId) => {
+ if(templateInstance.root_path.find(roleId => roleId==role.id)) {
+ if (role.assigned) {
+ templateInstance.assigned = true;
+ templateInstance.enabled = false
+ } else {
+ templateInstance.enabled = true;
+ templateInstance.assigned=templateInstance.assigned_origin
+ }
+ }
+ }
+ )
+ })
+ }
+ }
+
+ isAnyAssigned() : boolean {
+ if (Array.from(this.currentRoles.baseRoles.values()).find(role=>role.assigned)!=null) {
+ return true;
+ }
+ return Array.from(this.currentRoles.templateRoleInstances.values()).map((roleMap: Map<string, Role>) => Array.from(roleMap.values()))
+ .find(values=>values.find(role=>role.assigned))!=null
+ }
+
+
+
+
+ changeTemplateAssignment(role : Role, event) {
+ if (role.assigned) {
+ if (this.guest.assigned) {
+ this.guest.assigned = false;
+ this.guest.enabled = true;
}
+ if (!this.registered.assigned) {
+ this.registered.assigned=true;
+ }
+ } else {
+ if (!this.isAnyAssigned()) {
+ this.guest.assigned=true;
+ this.guest.enabled = false;
+ }
+
}
}
- changeInstAssignment(role : Role, event) {
- console.log("Change " + role.id);
- console.log("Assignment changed "+JSON.stringify(event));
- console.log("Event target "+event.target);
+ getInstanceContent(template:RoleTemplate, roles:Map<string,Role>) : Role {
+ return roles.get(template.id)
+ }
+
+ saveAssignments() {
+ this.saved=false;
+ this.success=true;
+ this.errors = [];
+ let assignmentMap : Map<string, Role> = new Map(this.currentRoles.baseRoles.filter(role => role.assigned != role.assigned_origin).map(role => [role.id, role]));
+ let assignments : Array<Role> = []
+ let unassignments : Array<Role> = []
+ assignmentMap.forEach((role, roleId)=>{
+ if (role.level>0) {
+ for(let parentId of role.root_path) {
+ if (assignmentMap.has(parentId) && assignmentMap.get(parentId).assigned) {
+ return;
+ }
+ }
+ }
+ if (role.assigned) {
+ assignments.push(role);
+ } else {
+ unassignments.push(role);
+ }
+ })
+ this.currentRoles.templateRoleInstances.forEach((templMap, resource)=> {
+ templMap.forEach((role, modelId)=> {
+ if (role.assigned!=role.assigned_origin) {
+ if (role.level>0) {
+ for(let parentId of role.root_path) {
+ if (assignmentMap.has(parentId) && assignmentMap.get(parentId).assigned) {
+ return;
+ }
+ }
+ }
+ if (role.assigned) {
+ assignments.push(role);
+ } else {
+ unassignments.push(role);
+ }
+ }
+ })
+ }
+ )
+ fromArray(assignments).pipe(switchMap((role) => this.roleService.assignRole(role.id, this.userid)),
+ catchError((err: ErrorResult, caught) => {
+ this.success = false;
+ this.errors.push(err);
+ return [];
+ }
+ )
+ ).subscribe((result:HttpResponse<Role>)=> {
+ this.updateRole(result.body, true);
+ this.saved=true;
+ }
+ );
+ fromArray(unassignments).pipe(switchMap((role) => this.roleService.unAssignRole(role.id, this.userid)),
+ catchError((err: ErrorResult, caught) => {
+ this.success = false;
+ this.errors.push(err);
+ return [];
+ }
+ )
+ ).subscribe(result=>{
+ this.updateRole(result.body,false);
+ this.saved=true;
+ });
+ this.saved=true;
+ }
+
+ private updateRole(role:Role, assignment:boolean) : void {
+ if (role!=null) {
+ if (role.template_instance) {
+ this.currentRoles.templateRoleInstances.forEach((templMap, resource)=>{
+ templMap.forEach((tmplRole, modelId)=> {
+ if (tmplRole.id == role.id) {
+ Util.deepCopy(role, tmplRole, false);
+ tmplRole.assigned = assignment;
+ }
+ }
+ )
+ })
+ } else {
+ let target = this.currentRoles.baseRoles.find(baseRole => baseRole.id == role.id);
+ Util.deepCopy(role, target, false);
+ target.assigned = assignment;
+ }
+ }
}
- getInstanceContent(template:RoleTemplate, roles:Array<Role>) : Role {
- return roles.find(role=>role.model_id==template.id)
+ ngAfterViewInit(): void {
+ this.roles$.subscribe(roleResult => this.currentRoles = roleResult);
}
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {Role} from "@app/model/role";
+
+export class RoleResult {
+ // Map of (roleId, Role)
+ baseRoles: Array<Role> = [];
+ // Map of (resource, [(modelId, role), ...])
+ templateRoleInstances: Map<string, Map<string, Role>> = new Map<string, Map<string, Role>>();
+}
\ No newline at end of file
~ with the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
- ~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
<a class="nav-link" routerLink="/security/users/add" routerLinkActive="active" href="#">{{'users.add.head' |translate }}</a>
</li>
<li class="nav-item">
- <a class="nav-link" routerLink="/security/users/edit" routerLinkActive="active" href="#">{{'users.edit.head' |translate }}</a>
+ <a class="nav-link" routerLink="/security/users/edit{{userId$|async}}" routerLinkActive="active" href="#">{{'users.edit.head' |translate }}</a>
</li>
<li class="nav-item">
- <a class="nav-link" routerLink="/security/users/roles" routerLinkActive="active" href="#">{{'users.roles.head' |translate }}</a>
+ <a class="nav-link" routerLink="/security/users/roles{{userId$|async}}" routerLinkActive="active" href="#">{{'users.roles.head' |translate }}</a>
</li>
</ul>
-<router-outlet ></router-outlet>
\ No newline at end of file
+<router-outlet (activate)="onChildActivate($event)" ></router-outlet>
\ No newline at end of file
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
- *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
- *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
- *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* under the License.
*/
-import { Component, OnInit } from '@angular/core';
+import {AfterViewInit, Component, OnInit} from '@angular/core';
+import {ActivatedRoute, Params} from "@angular/router";
+import {iif, Observable, of, pipe, merge, combineLatest} from "rxjs";
+import {combineAll, filter, map, mergeMap, share, switchMap, tap} from 'rxjs/operators';
+import {flatMap} from "rxjs/internal/operators";
+import {fromArray} from "rxjs/internal/observable/fromArray";
@Component({
selector: 'app-manage-users',
})
export class ManageUsersComponent implements OnInit {
- constructor() { }
+ userId$:Observable<string>
+
+ constructor(private route : ActivatedRoute) {
+ }
ngOnInit(): void {
}
+
+
+ onChildActivate(componentReference) {
+ // console.log("Activating "+componentReference+" - "+JSON.stringify(componentReference,getCircularReplacer()))
+ if (componentReference.userIdEvent!=null) {
+ let componentEmit : Observable<string> = componentReference.userIdEvent.pipe(
+ tap(userid=>console.log("Event "+componentReference.class+" "+userid)),
+ map((userid: string) => this.getSubPath(userid)));
+ if (this.userId$!=null) {
+ this.userId$ = merge(this.userId$, componentEmit)
+ } else {
+ this.userId$ = componentEmit;
+ }
+ }
+ }
+
+ getSubPath(userid:string) {
+ if (userid!=null && userid.length>0) {
+ return '/' + userid;
+ } else {
+ return '';
+ }
+ }
+
}
+
+const getCircularReplacer = () => {
+ const seen = new WeakSet();
+ return (key, value) => {
+ if (typeof value === "object" && value !== null) {
+ if (seen.has(value)) {
+ return;
+ }
+ seen.add(value);
+ }
+ return value;
+ };
+};
\ No newline at end of file
}
export const Util = {
- deepCopy(src: Object, dst: Object) {
+ deepCopy(src: Object, dst: Object, overwriteWithEmptyString:boolean=true) {
Object.keys(src).forEach((key, idx) => {
let srcEl = src[key];
if (typeof (srcEl) == 'object') {
}
dstEl = dst[key];
this.deepCopy(srcEl, dstEl);
+ } else if (typeof(srcEl)=='string') {
+ if (overwriteWithEmptyString) {
+ dst[key]=srcEl
+ } else {
+ if ((srcEl as string).length>0) {
+ dst[key]=srcEl
+ }
+ }
} else {
// console.debug("setting " + key + " = " + srcEl);
dst[key] = srcEl;
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
import { TestBed } from '@angular/core/testing';
import { RoleService } from './role.service';
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
import { Injectable } from '@angular/core';
import {ArchivaRequestService} from "@app/services/archiva-request.service";
import {RoleTemplate} from "@app/model/role-template";
import { Observable } from 'rxjs';
+import { Role } from '@app/model/role';
+import {HttpResponse} from "@angular/common/http";
@Injectable({
providedIn: 'root'
return this.rest.executeRestCall("get", "redback", "roles/templates", null);
}
+ public assignRole(roleId, userId) : Observable<HttpResponse<Role>> {
+ return this.rest.executeResponseCall<Role>("put", "redback", "roles/" + roleId + "/user/" + userId, null);
+ }
+
+ public unAssignRole(roleId, userId) : Observable<HttpResponse<Role>> {
+ return this.rest.executeResponseCall<Role>("delete", "redback", "roles/" + roleId + "/user/" + userId, null);
+ }
+
}
}
},
"roles": {
- "head": "Edit Roles"
+ "head": "Edit Roles",
+ "base_roles": "Roles",
+ "template_roles": "Repository Roles",
+ "table": {
+ "role": "Role",
+ "scope": "Scope",
+ "assign": "Assign",
+ "repository": "Repository"
+ }
}
},
"search": {
},
"button": {
"yes": "Yes",
- "no": "No"
+ "no": "No",
+ "save": "Save Changes"
}
},
"password": {