mirror of https://github.com/apache/archiva.git
Adding permission handling to webapp
This commit is contained in:
parent
e597490140
commit
2313e1cd86
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="navbarsDefault">
|
<div class="collapse navbar-collapse" id="navbarsDefault">
|
||||||
<div class="navbar-nav ml-auto">
|
<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}}
|
{{user.userInfo.fullName}}
|
||||||
</span>
|
</span>
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
|
@ -38,12 +38,12 @@
|
||||||
<i class="fas fa-home mr-1"></i>{{ 'menu.home' |translate }}
|
<i class="fas fa-home mr-1"></i>{{ 'menu.home' |translate }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</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">
|
<a class="nav-link" routerLink="/login" data-toggle="modal" data-target="#loginModal">
|
||||||
<i class="fas fa-user mr-1"></i>{{'menu.login' | translate}}
|
<i class="fas fa-user mr-1"></i>{{'menu.login' | translate}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</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()">
|
<a class="nav-link" routerLink="/logout" (click)="auth.logout()">
|
||||||
<i class="fas fa-user mr-1"></i>{{'menu.logout' | translate}}
|
<i class="fas fa-user mr-1"></i>{{'menu.logout' | translate}}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -62,17 +62,17 @@ export class AppComponent implements OnInit, OnDestroy{
|
||||||
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
let lang = this.user.userInfo.language;
|
if (this.user.userInfo!=null && this.user.userInfo.language!=null ) {
|
||||||
if (lang==null) {
|
this.translate.use(this.user.userInfo.language);
|
||||||
this.translate.use('en');
|
|
||||||
} else {
|
} else {
|
||||||
this.translate.use(lang);
|
this.translate.use('en');
|
||||||
}
|
}
|
||||||
// Subscribe to login event in authenticator to switch the language
|
// Subscribe to login event in authenticator to switch the language
|
||||||
this.auth.LoginEvent.subscribe(userInfo => {
|
this.auth.LoginEvent.subscribe(userInfo => {
|
||||||
if (userInfo.language != null) {
|
if (userInfo.language != null) {
|
||||||
this.switchLang(userInfo.language);
|
this.switchLang(userInfo.language);
|
||||||
}
|
}
|
||||||
|
// console.log("Permissions: " + JSON.stringify(this.user.permissions));
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { NotFoundComponent } from './modules/general/not-found/not-found.compone
|
||||||
import { SidemenuComponent } from './modules/general/sidemenu/sidemenu.component';
|
import { SidemenuComponent } from './modules/general/sidemenu/sidemenu.component';
|
||||||
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
import { LoginComponent } from './modules/general/login/login.component';
|
import { LoginComponent } from './modules/general/login/login.component';
|
||||||
|
import { ViewPermissionDirective } from './directives/view-permission.directive';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -41,6 +42,7 @@ import { LoginComponent } from './modules/general/login/login.component';
|
||||||
NotFoundComponent,
|
NotFoundComponent,
|
||||||
SidemenuComponent,
|
SidemenuComponent,
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
|
ViewPermissionDirective,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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 { ViewPermissionDirective } from './view-permission.directive';
|
||||||
|
|
||||||
|
describe('ViewPermissionDirective', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
const directive = new ViewPermissionDirective(null, null);
|
||||||
|
expect(directive).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -8,7 +8,6 @@
|
||||||
* with the License. You may obtain a copy of the License at
|
* 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,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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 { Operation } from './operation';
|
||||||
|
|
||||||
|
describe('Operation', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new Operation()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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 Operation {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
descriptionKey: string;
|
||||||
|
permanent: boolean;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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 { Permission } from './permission';
|
||||||
|
|
||||||
|
describe('Permission', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new Permission()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* 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 {Operation} from "./operation";
|
||||||
|
import {Resource} from "./resource";
|
||||||
|
|
||||||
|
export class Permission {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
permanent: boolean;
|
||||||
|
descriptionKey: string;
|
||||||
|
operation: Operation;
|
||||||
|
resource: Resource;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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 { Resource } from './resource';
|
||||||
|
|
||||||
|
describe('Resource', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new Resource()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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 Resource {
|
||||||
|
identifier: string;
|
||||||
|
permanent: boolean;
|
||||||
|
pattern: boolean;
|
||||||
|
}
|
|
@ -28,7 +28,6 @@ export class UserInfo {
|
||||||
timestampAccountCreation:Date;
|
timestampAccountCreation:Date;
|
||||||
timestampLastLogin:Date;
|
timestampLastLogin:Date;
|
||||||
timestampLastPasswordChange:Date;
|
timestampLastPasswordChange:Date;
|
||||||
assignedRoles:string[];
|
|
||||||
readOnly:boolean;
|
readOnly:boolean;
|
||||||
userManagerId:string;
|
userManagerId:string;
|
||||||
validationToken:string;
|
validationToken:string;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
-->
|
-->
|
||||||
<nav class="nav flex-column nav-pills " role="tablist" aria-orientation="vertical">
|
<nav class="nav flex-column nav-pills " role="tablist" aria-orientation="vertical">
|
||||||
|
|
||||||
|
<div [appViewPermission]="perms.menu.repo.section">
|
||||||
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true" >Artifacts</a>
|
<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"
|
<a class="nav-link active my-0 py-0" href="#" data-toggle="pill"
|
||||||
|
@ -27,8 +28,10 @@
|
||||||
role="tab" aria-controls="v-pills-browse" aria-selected="false">Browse</a>
|
role="tab" aria-controls="v-pills-browse" aria-selected="false">Browse</a>
|
||||||
|
|
||||||
<a class="nav-link my-0 py-0" href="#" data-toggle="pill"
|
<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"
|
<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"
|
<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>
|
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"
|
<a class="nav-link my-0 py-0" href="#" data-toggle="pill"
|
||||||
role="tab" aria-controls="v-pills-browse" aria-selected="false">Reports</a>
|
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"
|
<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>
|
role="tab" aria-controls="v-pills-home" aria-selected="false">Users</a>
|
||||||
<a class="nav-link my-0 py-0" href="#" data-toggle="pill"
|
<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>
|
role="tab" aria-controls="v-pills-browse" aria-selected="false">Roles</a>
|
||||||
<a class="nav-link my-0 py-0" href="#" data-toggle="pill"
|
<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>
|
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"
|
<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>
|
role="tab" aria-controls="v-pills-home" aria-selected="false">Documentation</a>
|
||||||
<a class="nav-link my-0 py-0" href="#" data-toggle="pill"
|
<a class="nav-link my-0 py-0" href="#" data-toggle="pill"
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import {UserService} from "../../../services/user.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sidemenu',
|
selector: 'app-sidemenu',
|
||||||
|
@ -25,7 +26,11 @@ import { Component, OnInit } from '@angular/core';
|
||||||
})
|
})
|
||||||
export class SidemenuComponent implements OnInit {
|
export class SidemenuComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
perms;
|
||||||
|
|
||||||
|
constructor(private user: UserService) {
|
||||||
|
this.perms = user.uiPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,17 +33,22 @@ import {UserInfo} from "../model/user-info";
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AuthenticationService {
|
export class AuthenticationService {
|
||||||
loggedIn: boolean;
|
authenticated: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The LoginEvent is emitted, when a successful login happened. And the corresponding user info was retrieved.
|
* The LoginEvent is emitted, when a successful login happened. And the corresponding user info was retrieved.
|
||||||
*/
|
*/
|
||||||
public LoginEvent: EventEmitter<UserInfo> = new EventEmitter<UserInfo>();
|
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,
|
constructor(private rest: ArchivaRequestService,
|
||||||
private userService: UserService) {
|
private userService: UserService) {
|
||||||
this.loggedIn = false;
|
this.authenticated = false;
|
||||||
this.restoreLoginData();
|
this.restoreLoginData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,13 +60,35 @@ export class AuthenticationService {
|
||||||
let expDate = new Date(expirationDate);
|
let expDate = new Date(expirationDate);
|
||||||
let currentDate = new Date();
|
let currentDate = new Date();
|
||||||
if (currentDate < expDate) {
|
if (currentDate < expDate) {
|
||||||
this.loggedIn = true
|
|
||||||
let observer = this.userService.retrieveUserInfo();
|
let observer = this.userService.retrieveUserInfo();
|
||||||
observer.subscribe(userInfo =>
|
observer.subscribe({
|
||||||
|
next: (userInfo: UserInfo) => {
|
||||||
|
if (userInfo != null) {
|
||||||
|
let permObserver = this.userService.retrievePermissionInfo();
|
||||||
|
permObserver.subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.authenticated = true;
|
||||||
this.LoginEvent.emit(userInfo)
|
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());
|
localStorage.setItem("token_expire", dt.toISOString());
|
||||||
}
|
}
|
||||||
let userObserver = this.userService.retrieveUserInfo();
|
let userObserver = this.userService.retrieveUserInfo();
|
||||||
this.loggedIn = true;
|
this.authenticated = true;
|
||||||
userObserver.subscribe(userInfo =>
|
userObserver.subscribe(userInfo => {
|
||||||
this.LoginEvent.emit(userInfo));
|
if (userInfo != null) {
|
||||||
|
let permObserver = this.userService.retrievePermissionInfo();
|
||||||
|
permObserver.subscribe((perms) => {
|
||||||
|
this.LoginEvent.emit(userInfo);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
resultHandler("OK");
|
resultHandler("OK");
|
||||||
},
|
},
|
||||||
error: (err: HttpErrorResponse) => {
|
error: (err: HttpErrorResponse) => {
|
||||||
|
@ -104,7 +138,7 @@ export class AuthenticationService {
|
||||||
let result = err.error as ErrorResult
|
let result = err.error as ErrorResult
|
||||||
if (result.errorMessages != null) {
|
if (result.errorMessages != null) {
|
||||||
for (let msg of result.errorMessages) {
|
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);
|
resultHandler("ERROR", result.errorMessages);
|
||||||
} else {
|
} else {
|
||||||
|
@ -125,8 +159,9 @@ export class AuthenticationService {
|
||||||
localStorage.removeItem("access_token");
|
localStorage.removeItem("access_token");
|
||||||
localStorage.removeItem("refresh_token");
|
localStorage.removeItem("refresh_token");
|
||||||
localStorage.removeItem("token_expire");
|
localStorage.removeItem("token_expire");
|
||||||
this.loggedIn = false;
|
this.authenticated = false;
|
||||||
this.userService.resetUser();
|
this.userService.resetUser();
|
||||||
this.rest.resetToken();
|
this.rest.resetToken();
|
||||||
|
this.LogoutEvent.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,23 +16,77 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable, OnDestroy, OnInit} from '@angular/core';
|
||||||
import {ArchivaRequestService} from "./archiva-request.service";
|
import {ArchivaRequestService} from "./archiva-request.service";
|
||||||
import {UserInfo} from '../model/user-info';
|
import {UserInfo} from '../model/user-info';
|
||||||
import {HttpErrorResponse} from "@angular/common/http";
|
import {HttpErrorResponse} from "@angular/common/http";
|
||||||
import {ErrorResult} from "../model/error-result";
|
import {ErrorResult} from "../model/error-result";
|
||||||
import {Observable} from "rxjs";
|
import {Observable} from "rxjs";
|
||||||
|
import {Permission} from '../model/permission';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class UserService {
|
export class UserService implements OnInit, OnDestroy {
|
||||||
|
|
||||||
userInfo: UserInfo;
|
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) {
|
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.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.loadPersistedUserInfo();
|
||||||
}
|
}
|
||||||
this.persistUserInfo();
|
this.persistUserInfo();
|
||||||
|
this.authenticated = true;
|
||||||
resultObserver.next(this.userInfo);
|
resultObserver.next(this.userInfo);
|
||||||
},
|
},
|
||||||
error: (err: HttpErrorResponse) => {
|
error: (err: HttpErrorResponse) => {
|
||||||
console.log("Error " + (JSON.stringify(err)));
|
console.log("Error " + (JSON.stringify(err)));
|
||||||
let result = err.error as ErrorResult
|
let result = err.error as ErrorResult
|
||||||
if (result.errorMessages != null) {
|
if (result != null && result.errorMessages != null) {
|
||||||
for (let msg of result.errorMessages) {
|
for (let msg of result.errorMessages) {
|
||||||
console.error('Observer got an error: ' + msg.errorKey)
|
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();
|
resultObserver.error();
|
||||||
},
|
},
|
||||||
complete: () => {
|
complete: () => {
|
||||||
|
@ -74,6 +132,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
|
* Stores user information persistent. Not the complete UserInfo object, only properties, that
|
||||||
* are needed.
|
* are needed.
|
||||||
|
@ -104,6 +252,9 @@ export class UserService {
|
||||||
*/
|
*/
|
||||||
resetUser() {
|
resetUser() {
|
||||||
this.userInfo = new UserInfo();
|
this.userInfo = new UserInfo();
|
||||||
|
this.userInfo.user_id = "guest";
|
||||||
|
this.resetPermissions();
|
||||||
|
this.authenticated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue