This is an automated email from the ASF dual-hosted git repository. martin_s pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/archiva.git
commit 2313e1cd86e8091bfcfe074b77e698becea8790e Author: Martin Stockhammer <[email protected]> AuthorDate: Wed Nov 4 16:20:31 2020 +0100 Adding permission handling to webapp --- .../main/archiva-web/src/app/app.component.html | 6 +- .../src/main/archiva-web/src/app/app.component.ts | 8 +- .../src/main/archiva-web/src/app/app.module.ts | 2 + .../view-permission.directive.spec.ts} | 20 +-- .../app/directives/view-permission.directive.ts | 64 +++++++++ .../main/archiva-web/src/app/model/access-token.ts | 3 +- .../model/{access-token.ts => operation.spec.ts} | 19 ++- .../app/model/{access-token.ts => operation.ts} | 16 +-- .../model/{access-token.ts => permission.spec.ts} | 19 ++- .../app/model/{access-token.ts => permission.ts} | 21 +-- .../model/{access-token.ts => resource.spec.ts} | 19 ++- .../src/app/model/{access-token.ts => resource.ts} | 15 +- .../main/archiva-web/src/app/model/user-info.ts | 1 - .../general/sidemenu/sidemenu.component.html | 16 ++- .../modules/general/sidemenu/sidemenu.component.ts | 7 +- .../src/app/services/authentication.service.ts | 55 +++++-- .../archiva-web/src/app/services/user.service.ts | 159 ++++++++++++++++++++- 17 files changed, 353 insertions(+), 97 deletions(-) diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.component.html index f223be3..fd24fdc 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.component.html +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.component.html @@ -29,7 +29,7 @@ <div class="collapse navbar-collapse" id="navbarsDefault"> <div class="navbar-nav ml-auto"> - <span *ngIf="auth.loggedIn" class="navbar-text border-right pr-2 mr-2"> + <span *ngIf="auth.authenticated" class="navbar-text border-right pr-2 mr-2"> {{user.userInfo.fullName}} </span> <ul class="navbar-nav"> @@ -38,12 +38,12 @@ <i class="fas fa-home mr-1"></i>{{ 'menu.home' |translate }} </a> </li> - <li *ngIf="!auth.loggedIn" class="nav-item active"> + <li *ngIf="!auth.authenticated" class="nav-item active"> <a class="nav-link" routerLink="/login" data-toggle="modal" data-target="#loginModal"> <i class="fas fa-user mr-1"></i>{{'menu.login' | translate}} </a> </li> - <li *ngIf="auth.loggedIn" class="nav-item active"> + <li *ngIf="auth.authenticated" class="nav-item active"> <a class="nav-link" routerLink="/logout" (click)="auth.logout()"> <i class="fas fa-user mr-1"></i>{{'menu.logout' | translate}} </a> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.component.ts index 3dae41b..e88a6a1 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.component.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.component.ts @@ -62,17 +62,17 @@ export class AppComponent implements OnInit, OnDestroy{ ngOnInit(): void { - let lang = this.user.userInfo.language; - if (lang==null) { - this.translate.use('en'); + if (this.user.userInfo!=null && this.user.userInfo.language!=null ) { + this.translate.use(this.user.userInfo.language); } else { - this.translate.use(lang); + this.translate.use('en'); } // Subscribe to login event in authenticator to switch the language this.auth.LoginEvent.subscribe(userInfo => { if (userInfo.language != null) { this.switchLang(userInfo.language); } + // console.log("Permissions: " + JSON.stringify(this.user.permissions)); }) } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts index aa38842..451e095 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts @@ -31,6 +31,7 @@ import { NotFoundComponent } from './modules/general/not-found/not-found.compone import { SidemenuComponent } from './modules/general/sidemenu/sidemenu.component'; import {FormsModule, ReactiveFormsModule} from "@angular/forms"; import { LoginComponent } from './modules/general/login/login.component'; +import { ViewPermissionDirective } from './directives/view-permission.directive'; @NgModule({ declarations: [ @@ -41,6 +42,7 @@ import { LoginComponent } from './modules/general/login/login.component'; NotFoundComponent, SidemenuComponent, LoginComponent, + ViewPermissionDirective, ], imports: [ BrowserModule, diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/directives/view-permission.directive.spec.ts similarity index 70% copy from archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts copy to archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/directives/view-permission.directive.spec.ts index 6204371..9c3bb9b 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/directives/view-permission.directive.spec.ts @@ -7,8 +7,7 @@ * "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 @@ -16,11 +15,12 @@ * specific language governing permissions and limitations * under the License. */ -export class AccessToken { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; - scope: string; - state: string; -} + +import { ViewPermissionDirective } from './view-permission.directive'; + +describe('ViewPermissionDirective', () => { + it('should create an instance', () => { + const directive = new ViewPermissionDirective(null, null); + expect(directive).toBeTruthy(); + }); +}); diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/directives/view-permission.directive.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/directives/view-permission.directive.ts new file mode 100644 index 0000000..70aeadb --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/directives/view-permission.directive.ts @@ -0,0 +1,64 @@ +/* + * 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 {Directive, ElementRef, Input, OnChanges, OnInit, Renderer2, SimpleChanges} from '@angular/core'; + +/** + * This directive can be used to render based on permissions + */ +@Directive({ + selector: '[appViewPermission]' +}) +export class ViewPermissionDirective implements OnInit, OnChanges { + @Input('appViewPermission') permission: boolean; + + constructor(private renderer: Renderer2, private el: ElementRef) { + + } + + ngOnInit(): void { + // console.log("Init appViewPermission " + this.permission + " " + typeof (this.permission)); + // this.togglePermission(); + } + + private togglePermission() { + if (this.permission) { + this.removeClass("d-none"); + } else { + this.addClass("d-none"); + } + } + + addClass(className: string) { + // make sure you declare classname in your main style.css + this.renderer.addClass(this.el.nativeElement, className); + } + + removeClass(className: string) { + this.renderer.removeClass(this.el.nativeElement, className); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.permission != null && + (changes.permission.firstChange || changes.permission.currentValue != changes.permission.previousValue)) { + // console.debug("Changed " + JSON.stringify(changes)); + this.togglePermission(); + } + } + +} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts index 6204371..9b3649d 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts @@ -7,8 +7,7 @@ * "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 diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/operation.spec.ts similarity index 77% copy from archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts copy to archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/operation.spec.ts index 6204371..35849e2 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/operation.spec.ts @@ -7,8 +7,7 @@ * "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 @@ -16,11 +15,11 @@ * specific language governing permissions and limitations * under the License. */ -export class AccessToken { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; - scope: string; - state: string; -} + +import { Operation } from './operation'; + +describe('Operation', () => { + it('should create an instance', () => { + expect(new Operation()).toBeTruthy(); + }); +}); diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/operation.ts similarity index 77% copy from archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts copy to archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/operation.ts index 6204371..eded8ae 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/operation.ts @@ -7,8 +7,7 @@ * "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 @@ -16,11 +15,10 @@ * specific language governing permissions and limitations * under the License. */ -export class AccessToken { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; - scope: string; - state: string; + +export class Operation { + name: string; + description: string; + descriptionKey: string; + permanent: boolean; } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/permission.spec.ts similarity index 77% copy from archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts copy to archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/permission.spec.ts index 6204371..bf2e5af 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/permission.spec.ts @@ -7,8 +7,7 @@ * "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 @@ -16,11 +15,11 @@ * specific language governing permissions and limitations * under the License. */ -export class AccessToken { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; - scope: string; - state: string; -} + +import { Permission } from './permission'; + +describe('Permission', () => { + it('should create an instance', () => { + expect(new Permission()).toBeTruthy(); + }); +}); diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/permission.ts similarity index 72% copy from archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts copy to archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/permission.ts index 6204371..53d00cf 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/permission.ts @@ -7,8 +7,7 @@ * "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 @@ -16,11 +15,15 @@ * specific language governing permissions and limitations * under the License. */ -export class AccessToken { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; - scope: string; - state: string; + +import {Operation} from "./operation"; +import {Resource} from "./resource"; + +export class Permission { + name: string; + description: string; + permanent: boolean; + descriptionKey: string; + operation: Operation; + resource: Resource; } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/resource.spec.ts similarity index 77% copy from archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts copy to archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/resource.spec.ts index 6204371..10ed83c 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/resource.spec.ts @@ -7,8 +7,7 @@ * "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 @@ -16,11 +15,11 @@ * specific language governing permissions and limitations * under the License. */ -export class AccessToken { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; - scope: string; - state: string; -} + +import { Resource } from './resource'; + +describe('Resource', () => { + it('should create an instance', () => { + expect(new Resource()).toBeTruthy(); + }); +}); diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/resource.ts similarity index 77% copy from archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts copy to archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/resource.ts index 6204371..f00f39f 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/access-token.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/resource.ts @@ -7,8 +7,7 @@ * "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 @@ -16,11 +15,9 @@ * specific language governing permissions and limitations * under the License. */ -export class AccessToken { - access_token: string; - refresh_token: string; - expires_in: number; - token_type: string; - scope: string; - state: string; + +export class Resource { + identifier: string; + permanent: boolean; + pattern: boolean; } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user-info.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user-info.ts index 6356fe2..0ac142b 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user-info.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user-info.ts @@ -28,7 +28,6 @@ export class UserInfo { timestampAccountCreation:Date; timestampLastLogin:Date; timestampLastPasswordChange:Date; - assignedRoles:string[]; readOnly:boolean; userManagerId:string; validationToken:string; diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sidemenu/sidemenu.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sidemenu/sidemenu.component.html index 81b53df..c394e89 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sidemenu/sidemenu.component.html +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sidemenu/sidemenu.component.html @@ -18,19 +18,22 @@ --> <nav class="nav flex-column nav-pills " role="tablist" aria-orientation="vertical"> - <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Artifacts</a> + <div [appViewPermission]="perms.menu.repo.section"> + <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true" >Artifacts</a> <a class="nav-link active my-0 py-0" href="#" data-toggle="pill" - role="tab" aria-controls="v-pills-search" aria-selected="true">Search</a> + role="tab" aria-controls="v-pills-search" aria-selected="true" >Search</a> <a class="nav-link my-0 py-0" href="#" data-toggle="pill" role="tab" aria-controls="v-pills-browse" aria-selected="false">Browse</a> <a class="nav-link my-0 py-0" href="#" data-toggle="pill" - role="tab" aria-controls="v-pills-browse" aria-selected="false">Upload Artifact</a> - + role="tab" aria-controls="v-pills-browse" aria-selected="false" + [appViewPermission]="perms.menu.repo.upload">Upload Artifact</a> + </div> + <div [appViewPermission]="perms.menu.admin.section"> <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true" data-toggle="pill" - role="tab" aria-controls="v-pills-home" aria-selected="false">Administration</a> + role="tab" aria-controls="v-pills-home" aria-selected="false" >Administration</a> <a class="nav-link my-0 py-0" href="#" data-toggle="pill" role="tab" aria-controls="v-pills-browse" aria-selected="false">Repository Groups</a> <a class="nav-link my-0 py-0" href="#" data-toggle="pill" @@ -51,6 +54,8 @@ role="tab" aria-controls="v-pills-browse" aria-selected="false">UI Configuration</a> <a class="nav-link my-0 py-0" href="#" data-toggle="pill" role="tab" aria-controls="v-pills-browse" aria-selected="false">Reports</a> + </div> + <div [appViewPermission]="perms.menu.user.section"> <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true" data-toggle="pill" role="tab" aria-controls="v-pills-home" aria-selected="false">Users</a> <a class="nav-link my-0 py-0" href="#" data-toggle="pill" @@ -59,6 +64,7 @@ role="tab" aria-controls="v-pills-browse" aria-selected="false">Roles</a> <a class="nav-link my-0 py-0" href="#" data-toggle="pill" role="tab" aria-controls="v-pills-browse" aria-selected="false">Users Runtime Configuration</a> + </div> <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true" data-toggle="pill" role="tab" aria-controls="v-pills-home" aria-selected="false">Documentation</a> <a class="nav-link my-0 py-0" href="#" data-toggle="pill" diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sidemenu/sidemenu.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sidemenu/sidemenu.component.ts index a02692d..c4fa16c 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sidemenu/sidemenu.component.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/general/sidemenu/sidemenu.component.ts @@ -17,6 +17,7 @@ * under the License. */ import { Component, OnInit } from '@angular/core'; +import {UserService} from "../../../services/user.service"; @Component({ selector: 'app-sidemenu', @@ -25,7 +26,11 @@ import { Component, OnInit } from '@angular/core'; }) export class SidemenuComponent implements OnInit { - constructor() { } + perms; + + constructor(private user: UserService) { + this.perms = user.uiPermissions; + } ngOnInit(): void { } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/authentication.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/authentication.service.ts index ba4cdb1..b4a2478 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/authentication.service.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/authentication.service.ts @@ -33,17 +33,22 @@ import {UserInfo} from "../model/user-info"; providedIn: 'root' }) export class AuthenticationService { - loggedIn: boolean; + authenticated: boolean; /** * The LoginEvent is emitted, when a successful login happened. And the corresponding user info was retrieved. */ public LoginEvent: EventEmitter<UserInfo> = new EventEmitter<UserInfo>(); + /** + * The LogoutEvent is emitted, when the user has been logged out. + */ + public LogoutEvent: EventEmitter<any> = new EventEmitter<any>(); + constructor(private rest: ArchivaRequestService, private userService: UserService) { - this.loggedIn = false; + this.authenticated = false; this.restoreLoginData(); } @@ -55,12 +60,34 @@ export class AuthenticationService { let expDate = new Date(expirationDate); let currentDate = new Date(); if (currentDate < expDate) { - this.loggedIn = true let observer = this.userService.retrieveUserInfo(); - observer.subscribe(userInfo => - this.LoginEvent.emit(userInfo) + observer.subscribe({ + next: (userInfo: UserInfo) => { + if (userInfo != null) { + let permObserver = this.userService.retrievePermissionInfo(); + permObserver.subscribe({ + next: () => { + this.authenticated = true; + this.LoginEvent.emit(userInfo) + }, + error: (err) => { + console.debug("Error retrieving perms: " + JSON.stringify(err)); + } + } + ) + } + }, + error: (err: HttpErrorResponse) => { + console.debug("Error retrieving user info: " + JSON.stringify(err)); + this.logout(); + } + } ); + } else { + this.logout(); } + } else { + this.logout(); } } @@ -94,9 +121,16 @@ export class AuthenticationService { localStorage.setItem("token_expire", dt.toISOString()); } let userObserver = this.userService.retrieveUserInfo(); - this.loggedIn = true; - userObserver.subscribe(userInfo => - this.LoginEvent.emit(userInfo)); + this.authenticated = true; + userObserver.subscribe(userInfo => { + if (userInfo != null) { + let permObserver = this.userService.retrievePermissionInfo(); + permObserver.subscribe((perms) => { + this.LoginEvent.emit(userInfo); + } + ) + } + }); resultHandler("OK"); }, error: (err: HttpErrorResponse) => { @@ -104,7 +138,7 @@ export class AuthenticationService { let result = err.error as ErrorResult if (result.errorMessages != null) { for (let msg of result.errorMessages) { - console.error('Observer got an error: ' + msg.errorKey) + console.debug('Observer got an error: ' + msg.errorKey) } resultHandler("ERROR", result.errorMessages); } else { @@ -125,8 +159,9 @@ export class AuthenticationService { localStorage.removeItem("access_token"); localStorage.removeItem("refresh_token"); localStorage.removeItem("token_expire"); - this.loggedIn = false; + this.authenticated = false; this.userService.resetUser(); this.rest.resetToken(); + this.LogoutEvent.emit(); } } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts index 3360e55..a1cc652 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts @@ -16,23 +16,77 @@ * under the License. */ -import {Injectable} from '@angular/core'; +import {Injectable, OnDestroy, OnInit} from '@angular/core'; import {ArchivaRequestService} from "./archiva-request.service"; import {UserInfo} from '../model/user-info'; import {HttpErrorResponse} from "@angular/common/http"; import {ErrorResult} from "../model/error-result"; import {Observable} from "rxjs"; +import {Permission} from '../model/permission'; @Injectable({ providedIn: 'root' }) -export class UserService { +export class UserService implements OnInit, OnDestroy { userInfo: UserInfo; + permissions: Permission[]; + guestPermissions: Permission[]; + authenticated: boolean; + uiPermissionsDefault = { + 'menu': { + 'repo':{ + 'section':true, + 'browse':true, + 'search':true, + 'upload':false + }, + 'admin':{ + 'section':false, + 'config':false, + 'status':false, + 'reports':false + }, + 'user':{ + 'section':false, + 'manage':false, + 'roles':false, + 'config':false + } + } + }; + uiPermissions; constructor(private rest: ArchivaRequestService) { - this.userInfo = new UserInfo() + this.userInfo = new UserInfo(); + this.uiPermissions = {}; + this.deepCopy(this.uiPermissionsDefault, this.uiPermissions); + } + + ngOnDestroy(): void { + this.resetUser(); + } + + ngOnInit(): void { + this.userInfo.user_id = "guest"; this.loadPersistedUserInfo(); + this.authenticated = false; + this.deepCopy(this.uiPermissionsDefault, this.uiPermissions); + if (this.guestPermissions == null) { + let observer = { + next: (permList: Permission[]) => { + this.guestPermissions = permList; + if (!this.authenticated) { + this.permissions = this.guestPermissions; + this.parsePermissions(this.permissions); + } + }, + error: err => { + console.log("Could not retrieve permissions "+err); + } + } + this.retrievePermissionInfo().subscribe(observer); + } } /** @@ -53,16 +107,20 @@ export class UserService { this.loadPersistedUserInfo(); } this.persistUserInfo(); + this.authenticated = true; resultObserver.next(this.userInfo); }, error: (err: HttpErrorResponse) => { console.log("Error " + (JSON.stringify(err))); let result = err.error as ErrorResult - if (result.errorMessages != null) { + if (result != null && result.errorMessages != null) { for (let msg of result.errorMessages) { console.error('Observer got an error: ' + msg.errorKey) } + } else if (err.message != null) { + console.error("Bad response from user info call: " + err.message); } + this.authenticated = false; resultObserver.error(); }, complete: () => { @@ -75,6 +133,96 @@ export class UserService { } /** + * Retrieves the permission list from the REST service + */ + public retrievePermissionInfo(): Observable<Permission[]> { + return new Observable<Permission[]>((resultObserver) => { + let userName = this.authenticated ? "me" : "guest"; + let infoObserver = this.rest.executeRestCall<Permission[]>("get", "redback", "users/" + userName + "/permissions", null); + let permissionObserver = { + next: (x: Permission[]) => { + this.permissions = x; + this.parsePermissions(x); + resultObserver.next(this.permissions); + }, + error: (err: HttpErrorResponse) => { + console.log("Error " + (JSON.stringify(err))); + let result = err.error as ErrorResult + if (result.errorMessages != null) { + for (let msg of result.errorMessages) { + console.debug('Observer got an error: ' + msg.errorKey) + } + } + this.resetPermissions(); + resultObserver.error(err); + }, + complete: () => { + resultObserver.complete(); + } + }; + infoObserver.subscribe(permissionObserver); + + }); + } + + resetPermissions() { + this.deepCopy(this.uiPermissionsDefault, this.uiPermissions); + } + parsePermissions(permissions: Permission[]) { + this.resetPermissions(); + for ( let perm of permissions) { + // console.debug("Checking permission for op: " + perm.operation.name); + switch (perm.operation.name) { + case "archiva-manage-configuration": { + if (perm.resource.identifier=='*') { + this.uiPermissions.menu.admin.section = true; + this.uiPermissions.menu.admin.config = true; + this.uiPermissions.menu.admin.reports = true; + this.uiPermissions.menu.admin.status = true; + } + + } + case "archiva-manage-users": { + if (perm.resource.identifier=='*') { + this.uiPermissions.menu.user.section = true; + this.uiPermissions.menu.user.config = true; + this.uiPermissions.menu.user.manage = true; + this.uiPermissions.menu.user.roles = true; + } + } + case "redback-configuration-edit": { + if (perm.resource.identifier=='*') { + this.uiPermissions.menu.user.section = true; + this.uiPermissions.menu.user.config = true; + } + } + case "archiva-upload-file": { + this.uiPermissions.menu.repo.upload = true; + } + } + } + console.log("New permissions: " + JSON.stringify(this.uiPermissions)); + } + + private deepCopy(src: Object, dst: Object) { + Object.keys(src).forEach((key, idx) => { + let srcEl = src[key]; + if (typeof(srcEl)=='object' ) { + let dstEl; + if (!dst.hasOwnProperty(key)) { + dst[key] = {} + } + dstEl = dst[key]; + this.deepCopy(srcEl, dstEl); + } else { + // console.debug("setting " + key + " = " + srcEl); + dst[key] = srcEl; + } + }); + } + + + /** * Stores user information persistent. Not the complete UserInfo object, only properties, that * are needed. */ @@ -104,6 +252,9 @@ export class UserService { */ resetUser() { this.userInfo = new UserInfo(); + this.userInfo.user_id = "guest"; + this.resetPermissions(); + this.authenticated = false; } }
