@@ -19,11 +19,11 @@ | |||
import { NgModule } from '@angular/core'; | |||
import { Routes, RouterModule } from '@angular/router'; | |||
import { AboutComponent } from './modules/general/about/about.component'; | |||
import { ContactComponent } from './modules/general/contact/contact.component'; | |||
import { HomeComponent } from './modules/general/home/home.component'; | |||
import { NotFoundComponent } from './modules/general/not-found/not-found.component'; | |||
import { LoginComponent } from "./modules/general/login/login.component"; | |||
import { AboutComponent } from './modules/shared/about/about.component'; | |||
import { ContactComponent } from './modules/shared/contact/contact.component'; | |||
import { HomeComponent } from './modules/shared/home/home.component'; | |||
import { NotFoundComponent } from './modules/shared/not-found/not-found.component'; | |||
import { LoginComponent } from "./modules/shared/login/login.component"; | |||
import { SearchComponent } from './modules/repo/search/search.component'; | |||
import {BrowseComponent} from "./modules/repo/browse/browse.component"; | |||
import {UploadComponent} from "./modules/repo/upload/upload.component"; |
@@ -16,39 +16,29 @@ | |||
* specific language governing permissions and limitations | |||
* under the License. | |||
*/ | |||
import { BrowserModule } from '@angular/platform-browser'; | |||
import { NgModule } from '@angular/core'; | |||
import { HttpClient, HttpClientModule } from '@angular/common/http'; | |||
import { TranslateLoader, TranslateModule, TranslateCompiler } from '@ngx-translate/core'; | |||
import { TranslateHttpLoader } from '@ngx-translate/http-loader'; | |||
import { TranslateMessageFormatCompiler, MESSAGE_FORMAT_CONFIG } from 'ngx-translate-messageformat-compiler'; | |||
import {BrowserModule} from '@angular/platform-browser'; | |||
import {NgModule} from '@angular/core'; | |||
import {HttpClientModule} from '@angular/common/http'; | |||
import {MESSAGE_FORMAT_CONFIG} from 'ngx-translate-messageformat-compiler'; | |||
import { AppRoutingModule } from './app-routing.module'; | |||
import { AppComponent } from './app.component'; | |||
import { HomeComponent } from './modules/general/home/home.component'; | |||
import { ContactComponent } from './modules/general/contact/contact.component'; | |||
import { AboutComponent } from './modules/general/about/about.component'; | |||
import { NotFoundComponent } from './modules/general/not-found/not-found.component'; | |||
import { SidemenuComponent } from './modules/general/sidemenu/sidemenu.component'; | |||
import {AppRoutingModule} from './app-routing.module'; | |||
import {AppComponent} from './app.component'; | |||
import {HomeComponent} from './modules/shared/home/home.component'; | |||
import {ContactComponent} from './modules/shared/contact/contact.component'; | |||
import {AboutComponent} from './modules/shared/about/about.component'; | |||
import {NotFoundComponent} from './modules/shared/not-found/not-found.component'; | |||
import {SidemenuComponent} from './modules/shared/sidemenu/sidemenu.component'; | |||
import {FormsModule, ReactiveFormsModule} from "@angular/forms"; | |||
import { LoginComponent } from './modules/general/login/login.component'; | |||
import { ViewPermissionDirective } from './directives/view-permission.directive'; | |||
import { NavSubgroupDirective } from './directives/nav-subgroup.directive'; | |||
import { SearchComponent } from './modules/repo/search/search.component'; | |||
import { BrowseComponent } from './modules/repo/browse/browse.component'; | |||
import { UploadComponent } from './modules/repo/upload/upload.component'; | |||
import { ManageUsersComponent } from './modules/user/manage-users/manage-users.component'; | |||
import { ManageRolesComponent } from './modules/user/manage-roles/manage-roles.component'; | |||
import { SecurityConfigurationComponent } from './modules/user/security-configuration/security-configuration.component'; | |||
import { ManageUsersListComponent } from './modules/user/users/manage-users-list/manage-users-list.component'; | |||
import { ManageUsersAddComponent } from './modules/user/users/manage-users-add/manage-users-add.component'; | |||
import { NgbPaginationModule, NgbTooltipModule} from "@ng-bootstrap/ng-bootstrap"; | |||
import { PaginatedEntitiesComponent } from './modules/general/paginated-entities/paginated-entities.component'; | |||
import { SortedTableHeaderComponent } from './modules/general/sorted-table-header/sorted-table-header.component'; | |||
import { SortedTableHeaderRowComponent } from './modules/general/sorted-table-header-row/sorted-table-header-row.component'; | |||
import { ManageUsersEditComponent } from './modules/user/users/manage-users-edit/manage-users-edit.component'; | |||
import {ErrorHandlerModule} from "./modules/core/errors/error-handler.module"; | |||
import {LoginComponent} from './modules/shared/login/login.component'; | |||
import {ViewPermissionDirective} from './directives/view-permission.directive'; | |||
import {NavSubgroupDirective} from './directives/nav-subgroup.directive'; | |||
import {SearchComponent} from './modules/repo/search/search.component'; | |||
import {BrowseComponent} from './modules/repo/browse/browse.component'; | |||
import {UploadComponent} from './modules/repo/upload/upload.component'; | |||
import {SecurityConfigurationComponent} from './modules/user/security-configuration/security-configuration.component'; | |||
import {CoreModule} from "./modules/core/core.module"; | |||
import {SharedModule} from "./modules/shared/shared.module"; | |||
import {UserModule} from "./modules/user/user.module"; | |||
@NgModule({ | |||
@@ -65,15 +55,7 @@ import {CoreModule} from "./modules/core/core.module"; | |||
SearchComponent, | |||
BrowseComponent, | |||
UploadComponent, | |||
ManageUsersComponent, | |||
ManageRolesComponent, | |||
SecurityConfigurationComponent, | |||
ManageUsersListComponent, | |||
ManageUsersAddComponent, | |||
PaginatedEntitiesComponent, | |||
SortedTableHeaderComponent, | |||
SortedTableHeaderRowComponent, | |||
ManageUsersEditComponent, | |||
], | |||
imports: [ | |||
BrowserModule, | |||
@@ -81,20 +63,10 @@ import {CoreModule} from "./modules/core/core.module"; | |||
FormsModule, | |||
ReactiveFormsModule, | |||
HttpClientModule, | |||
TranslateModule.forRoot({ | |||
compiler: { | |||
provide: TranslateCompiler, | |||
useClass: TranslateMessageFormatCompiler | |||
}, | |||
loader: { | |||
provide: TranslateLoader, | |||
useFactory: httpTranslateLoader, | |||
deps: [HttpClient] | |||
} | |||
}), | |||
NgbPaginationModule, | |||
NgbTooltipModule, | |||
CoreModule | |||
CoreModule, | |||
SharedModule, | |||
UserModule | |||
], | |||
providers: [ | |||
{ provide: MESSAGE_FORMAT_CONFIG, useValue: { locales: ['en', 'de'] }} | |||
@@ -103,6 +75,3 @@ import {CoreModule} from "./modules/core/core.module"; | |||
}) | |||
export class AppModule { } | |||
export function httpTranslateLoader(http: HttpClient) { | |||
return new TranslateHttpLoader(http); | |||
} |
@@ -30,7 +30,7 @@ import { HttpErrorInterceptor } from './http-error-interceptor'; | |||
CommonModule, | |||
], | |||
providers: [ | |||
{ provide: ErrorHandler, useClass: GlobalErrorHandler }, | |||
// { provide: ErrorHandler, useClass: GlobalErrorHandler }, | |||
{ provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true } | |||
] | |||
}) |
@@ -0,0 +1,71 @@ | |||
/* | |||
* 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 { NgModule } from '@angular/core'; | |||
import { CommonModule } from '@angular/common'; | |||
import {PaginatedEntitiesComponent} from "./paginated-entities/paginated-entities.component"; | |||
import {SortedTableHeaderComponent} from "./sorted-table-header/sorted-table-header.component"; | |||
import {SortedTableHeaderRowComponent} from "./sorted-table-header-row/sorted-table-header-row.component"; | |||
import {NgbPaginationModule, NgbTooltipModule} from "@ng-bootstrap/ng-bootstrap"; | |||
import {TranslateCompiler, TranslateLoader, TranslateModule} from "@ngx-translate/core"; | |||
import {TranslateMessageFormatCompiler} from "ngx-translate-messageformat-compiler"; | |||
import {HttpClient} from "@angular/common/http"; | |||
import {TranslateHttpLoader} from "@ngx-translate/http-loader"; | |||
import {RouterModule} from "@angular/router"; | |||
import {FormsModule} from "@angular/forms"; | |||
@NgModule({ | |||
declarations: [ | |||
PaginatedEntitiesComponent, | |||
SortedTableHeaderComponent, | |||
SortedTableHeaderRowComponent | |||
], | |||
exports: [ | |||
CommonModule, | |||
RouterModule, | |||
TranslateModule, | |||
NgbPaginationModule, | |||
NgbTooltipModule, | |||
PaginatedEntitiesComponent, | |||
SortedTableHeaderComponent, | |||
SortedTableHeaderRowComponent | |||
], | |||
imports: [ | |||
CommonModule, | |||
RouterModule, | |||
NgbPaginationModule, | |||
NgbTooltipModule, | |||
TranslateModule.forRoot({ | |||
compiler: { | |||
provide: TranslateCompiler, | |||
useClass: TranslateMessageFormatCompiler | |||
}, | |||
loader: { | |||
provide: TranslateLoader, | |||
useFactory: httpTranslateLoader, | |||
deps: [HttpClient] | |||
} | |||
}), | |||
] | |||
}) | |||
export class SharedModule { } | |||
export function httpTranslateLoader(http: HttpClient) { | |||
return new TranslateHttpLoader(http); | |||
} |
@@ -0,0 +1,50 @@ | |||
/* | |||
* 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 { NgModule } from '@angular/core'; | |||
import { CommonModule } from '@angular/common'; | |||
import {ManageUsersComponent} from "./manage-users/manage-users.component"; | |||
import {ManageUsersListComponent} from "./users/manage-users-list/manage-users-list.component"; | |||
import {ManageUsersAddComponent} from "./users/manage-users-add/manage-users-add.component"; | |||
import {ManageUsersEditComponent} from "./users/manage-users-edit/manage-users-edit.component"; | |||
import {SharedModule} from "../shared/shared.module"; | |||
import {FormsModule, ReactiveFormsModule} from "@angular/forms"; | |||
@NgModule({ | |||
declarations: [ | |||
ManageUsersComponent, | |||
ManageUsersListComponent, | |||
ManageUsersAddComponent, | |||
ManageUsersEditComponent | |||
], | |||
exports: [ | |||
ManageUsersComponent, | |||
ManageUsersListComponent, | |||
ManageUsersAddComponent, | |||
ManageUsersEditComponent | |||
], | |||
imports: [ | |||
CommonModule, | |||
SharedModule, | |||
FormsModule, | |||
ReactiveFormsModule | |||
] | |||
}) | |||
export class UserModule { } |
@@ -18,54 +18,22 @@ | |||
*/ | |||
import {Component, OnInit} from '@angular/core'; | |||
import { | |||
FormBuilder, | |||
FormGroup, | |||
Validators, | |||
FormControl, | |||
AsyncValidator, | |||
AbstractControl, | |||
ValidationErrors, | |||
ValidatorFn | |||
} from '@angular/forms'; | |||
import {AbstractControl, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn} from '@angular/forms'; | |||
import {UserService} from "../../../../services/user.service"; | |||
import {User} from "../../../../model/user"; | |||
import {ErrorResult} from "../../../../model/error-result"; | |||
import {catchError, debounceTime, distinctUntilChanged, map, switchMap} from "rxjs/operators"; | |||
import {throwError, Observable, of, pipe, timer} from 'rxjs'; | |||
import {environment} from "../../../../../environments/environment"; | |||
import {catchError} from "rxjs/operators"; | |||
import {UserInfo} from "../../../../model/user-info"; | |||
import {ManageUsersBaseComponent} from "../manage-users-base.component"; | |||
@Component({ | |||
selector: 'app-manage-users-add', | |||
templateUrl: './manage-users-add.component.html', | |||
styleUrls: ['./manage-users-add.component.scss'] | |||
}) | |||
export class ManageUsersAddComponent implements OnInit { | |||
export class ManageUsersAddComponent extends ManageUsersBaseComponent implements OnInit { | |||
editProperties = ['user_id', 'full_name', 'email', 'locked', 'password_change_required', | |||
'password', 'confirm_password', 'validated']; | |||
minUserIdSize = environment.application.minUserIdLength; | |||
success: boolean = false; | |||
error: boolean = false; | |||
errorResult: ErrorResult; | |||
result: UserInfo; | |||
user: string; | |||
userForm = this.fb.group({ | |||
user_id: ['', [Validators.required, Validators.minLength(this.minUserIdSize), whitespaceValidator()],this.userUidExistsValidator()], | |||
full_name: ['', Validators.required], | |||
email: ['', [Validators.required, Validators.email]], | |||
locked: [false], | |||
password_change_required: [true], | |||
password: [''], | |||
confirm_password: [''], | |||
validated: [true] | |||
}, { | |||
validator: MustMatch('password', 'confirm_password') | |||
}) | |||
constructor(public userService: UserService, public fb: FormBuilder) { | |||
constructor(userService: UserService, fb: FormBuilder) { | |||
super(userService, fb); | |||
} | |||
@@ -104,40 +72,6 @@ export class ManageUsersAddComponent implements OnInit { | |||
} | |||
} | |||
public copyFromForm(properties: string[]): User { | |||
let user: any = new User(); | |||
for (let prop of properties) { | |||
user[prop] = this.userForm.get(prop).value; | |||
} | |||
console.log("User " + user); | |||
return user; | |||
} | |||
public copyToForm(properties: string[], user: User): void { | |||
let propMap = {}; | |||
for (let prop of properties) { | |||
let propValue = user[prop] == null ? '' : user[prop]; | |||
propMap[prop] = propValue; | |||
} | |||
this.userForm.patchValue(propMap); | |||
console.log("User " + user); | |||
} | |||
valid(field: string): string[] { | |||
let formField = this.userForm.get(field); | |||
if (formField.dirty || formField.touched) { | |||
if (formField.valid) { | |||
return ['is-valid'] | |||
} else { | |||
return ['is-invalid'] | |||
} | |||
} else { | |||
return [''] | |||
} | |||
} | |||
getAllErrors(formGroup: FormGroup, errors: string[] = []) : string[] { | |||
Object.keys(formGroup.controls).forEach(field => { | |||
const control = formGroup.get(field); | |||
@@ -155,56 +89,9 @@ export class ManageUsersAddComponent implements OnInit { | |||
return Object.keys(this.userForm.get(control).errors); | |||
} | |||
/** | |||
* Async validator with debounce time | |||
* @constructor | |||
*/ | |||
userUidExistsValidator() { | |||
return (ctrl : FormControl) => { | |||
// debounceTimer() does not work here, as the observable is created with each keystroke | |||
// but angular does unsubscribe on previous started async observables. | |||
return timer(500).pipe( | |||
switchMap((userid) => this.userService.userExists(ctrl.value)), | |||
catchError(() => of(null)), | |||
map(exists => (exists ? {userexists: true} : null)) | |||
); | |||
} | |||
} | |||
forbiddenNameValidator(nameRe: RegExp): ValidatorFn { | |||
return (control: AbstractControl): {[key: string]: any} | null => { | |||
const forbidden = nameRe.test(control.value); | |||
return forbidden ? {forbiddenName: {value: control.value}} : null; | |||
}; | |||
} | |||
} | |||
export function whitespaceValidator(): ValidatorFn { | |||
return (control: AbstractControl): ValidationErrors | null => { | |||
const hasWhitespace = /\s/g.test(control.value); | |||
return hasWhitespace ? {containsWhitespace: {value: control.value}} : null; | |||
}; | |||
} | |||
export function MustMatch(controlName: string, matchingControlName: string) : ValidatorFn { | |||
return (formGroup: FormGroup): ValidationErrors | null => { | |||
const control = formGroup.controls[controlName]; | |||
const matchingControl = formGroup.controls[matchingControlName]; | |||
if (matchingControl.errors && !matchingControl.errors.mustMatch) { | |||
// return if another validator has already found an error on the matchingControl | |||
return; | |||
} | |||
// set error on matchingControl if validation fails | |||
if (control.value !== matchingControl.value) { | |||
matchingControl.setErrors({mustMatch: true}); | |||
} else { | |||
matchingControl.setErrors(null); | |||
} | |||
} | |||
} | |||
@@ -0,0 +1,145 @@ | |||
/* | |||
* 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 {environment} from "../../../../environments/environment"; | |||
import {ErrorResult} from "../../../model/error-result"; | |||
import {UserInfo} from "../../../model/user-info"; | |||
import { | |||
AbstractControl, | |||
FormControl, | |||
ValidatorFn, | |||
Validators, | |||
FormBuilder, | |||
ValidationErrors, | |||
FormGroup | |||
} from "@angular/forms"; | |||
import {User} from "../../../model/user"; | |||
import {of, timer} from "rxjs"; | |||
import {catchError, map, switchMap} from "rxjs/operators"; | |||
import { UserService } from 'src/app/services/user.service'; | |||
export class ManageUsersBaseComponent { | |||
editProperties = ['user_id', 'full_name', 'email', 'locked', 'password_change_required', | |||
'password', 'confirm_password', 'validated']; | |||
minUserIdSize = environment.application.minUserIdLength; | |||
success: boolean = false; | |||
error: boolean = false; | |||
errorResult: ErrorResult; | |||
result: UserInfo; | |||
user: string; | |||
userForm = this.fb.group({ | |||
user_id: ['', [Validators.required, Validators.minLength(this.minUserIdSize), whitespaceValidator()], this.userUidExistsValidator()], | |||
full_name: ['', Validators.required], | |||
email: ['', [Validators.required, Validators.email]], | |||
locked: [false], | |||
password_change_required: [true], | |||
password: [''], | |||
confirm_password: [''], | |||
validated: [true] | |||
}, { | |||
validator: MustMatch('password', 'confirm_password') | |||
}) | |||
constructor(public userService: UserService, public fb: FormBuilder) { | |||
} | |||
public copyFromForm(properties: string[]): User { | |||
let user: any = new User(); | |||
for (let prop of properties) { | |||
user[prop] = this.userForm.get(prop).value; | |||
} | |||
console.log("User " + user); | |||
return user; | |||
} | |||
public copyToForm(properties: string[], user: User): void { | |||
let propMap = {}; | |||
for (let prop of properties) { | |||
let propValue = user[prop] == null ? '' : user[prop]; | |||
propMap[prop] = propValue; | |||
} | |||
this.userForm.patchValue(propMap); | |||
console.log("User " + user); | |||
} | |||
valid(field: string): string[] { | |||
let formField = this.userForm.get(field); | |||
if (formField.dirty || formField.touched) { | |||
if (formField.valid) { | |||
return ['is-valid'] | |||
} else { | |||
return ['is-invalid'] | |||
} | |||
} else { | |||
return [''] | |||
} | |||
} | |||
/** | |||
* Async validator with debounce time | |||
* @constructor | |||
*/ | |||
userUidExistsValidator() { | |||
return (ctrl: FormControl) => { | |||
// debounceTimer() does not work here, as the observable is created with each keystroke | |||
// but angular does unsubscribe on previous started async observables. | |||
return timer(500).pipe( | |||
switchMap((userid) => this.userService.userExists(ctrl.value)), | |||
catchError(() => of(null)), | |||
map(exists => (exists ? {userexists: true} : null)) | |||
); | |||
} | |||
} | |||
forbiddenNameValidator(nameRe: RegExp): ValidatorFn { | |||
return (control: AbstractControl): { [key: string]: any } | null => { | |||
const forbidden = nameRe.test(control.value); | |||
return forbidden ? {forbiddenName: {value: control.value}} : null; | |||
}; | |||
} | |||
} | |||
export function whitespaceValidator(): ValidatorFn { | |||
return (control: AbstractControl): ValidationErrors | null => { | |||
const hasWhitespace = /\s/g.test(control.value); | |||
return hasWhitespace ? {containsWhitespace: {value: control.value}} : null; | |||
}; | |||
} | |||
export function MustMatch(controlName: string, matchingControlName: string) : ValidatorFn { | |||
return (formGroup: FormGroup): ValidationErrors | null => { | |||
const control = formGroup.controls[controlName]; | |||
const matchingControl = formGroup.controls[matchingControlName]; | |||
if (matchingControl.errors && !matchingControl.errors.mustMatch) { | |||
// return if another validator has already found an error on the matchingControl | |||
return; | |||
} | |||
// set error on matchingControl if validation fails | |||
if (control.value !== matchingControl.value) { | |||
matchingControl.setErrors({mustMatch: true}); | |||
} else { | |||
matchingControl.setErrors(null); | |||
} | |||
} | |||
} |
@@ -19,17 +19,16 @@ | |||
import { Component, OnInit } from '@angular/core'; | |||
import { ActivatedRoute } from '@angular/router'; | |||
import {UserService} from "../../../../services/user.service"; | |||
import {FormBuilder, FormControl, Validators} from "@angular/forms"; | |||
import {ManageUsersAddComponent, MustMatch} from "../manage-users-add/manage-users-add.component"; | |||
import {environment} from "../../../../../environments/environment"; | |||
import {FormBuilder, FormControl} from "@angular/forms"; | |||
import {map, switchMap} from 'rxjs/operators'; | |||
import {ManageUsersBaseComponent} from "../manage-users-base.component"; | |||
@Component({ | |||
selector: 'app-manage-users-edit', | |||
templateUrl: './manage-users-edit.component.html', | |||
styleUrls: ['./manage-users-edit.component.scss'] | |||
}) | |||
export class ManageUsersEditComponent extends ManageUsersAddComponent implements OnInit { | |||
export class ManageUsersEditComponent extends ManageUsersBaseComponent implements OnInit { | |||
editProperties = ['user_id', 'full_name', 'email', 'locked', 'password_change_required', | |||
'password', 'confirm_password', 'validated']; |
@@ -34,6 +34,7 @@ import {UserInfo} from "../model/user-info"; | |||
}) | |||
export class AuthenticationService { | |||
authenticated: boolean; | |||
authenticating: boolean; | |||
/** | |||
* The LoginEvent is emitted, when a successful login happened. And the corresponding user info was retrieved. | |||
@@ -49,10 +50,17 @@ export class AuthenticationService { | |||
constructor(private rest: ArchivaRequestService, | |||
private userService: UserService) { | |||
this.authenticated = false; | |||
this.LoginEvent.subscribe((info)=>{ | |||
this.authenticating=false; | |||
}) | |||
this.LogoutEvent.subscribe(()=>{ | |||
this.authenticating=false; | |||
}) | |||
this.restoreLoginData(); | |||
} | |||
private restoreLoginData() { | |||
console.debug("Restoring login data"); | |||
let accessToken = localStorage.getItem("access_token"); | |||
if (accessToken != null) { | |||
let expirationDate = localStorage.getItem("token_expire"); | |||
@@ -60,6 +68,8 @@ export class AuthenticationService { | |||
let expDate = new Date(expirationDate); | |||
let currentDate = new Date(); | |||
if (currentDate < expDate) { | |||
console.debug("Retrieving user information"); | |||
this.authenticating=true; | |||
let observer = this.userService.retrieveUserInfo(); | |||
observer.subscribe({ | |||
next: (userInfo: UserInfo) => { | |||
@@ -80,6 +90,9 @@ export class AuthenticationService { | |||
error: (err: HttpErrorResponse) => { | |||
console.debug("Error retrieving user info: " + JSON.stringify(err)); | |||
this.logout(); | |||
}, | |||
complete: () => { | |||
this.authenticating=false; | |||
} | |||
} | |||
); | |||
@@ -104,7 +117,7 @@ export class AuthenticationService { | |||
* @param resultHandler A result handler that is executed, after calling the login service | |||
*/ | |||
login(userid: string, password: string, resultHandler: (n: string, err?: ErrorMessage[]) => void) { | |||
this.authenticating=true; | |||
const data = { | |||
'grant_type': 'authorization_code', | |||
'client_id': environment.application.client_id, | |||
@@ -134,6 +147,7 @@ export class AuthenticationService { | |||
resultHandler("OK"); | |||
}, | |||
error: (err: HttpErrorResponse) => { | |||
this.authenticating = false; | |||
console.log("Error " + (JSON.stringify(err))); | |||
let result = err.error as ErrorResult | |||
if (result.error_messages != null) { | |||
@@ -146,7 +160,9 @@ export class AuthenticationService { | |||
} | |||
}, | |||
// complete: () => console.log('Observer got a complete notification'), | |||
complete: () => { | |||
this.authenticating = false; | |||
} | |||
}; | |||
authObserver.subscribe(tokenObserver) | |||
@@ -16,9 +16,13 @@ | |||
* under the License. | |||
*/ | |||
import { Injectable } from '@angular/core'; | |||
import {Injectable, OnInit} from '@angular/core'; | |||
import {UserService} from "./user.service"; | |||
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router} from "@angular/router"; | |||
import {AuthenticationService} from "./authentication.service"; | |||
import {first, timeout, tap, map, take} from "rxjs/operators"; | |||
import { Observable } from 'rxjs'; | |||
import { UserInfo } from '../model/user-info'; | |||
/** | |||
* Guard for the routes, that checks permissions by querying the uiPermission map of the UserService. | |||
@@ -28,18 +32,37 @@ import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router} from " | |||
@Injectable({ | |||
providedIn: 'root' | |||
}) | |||
export class RoutingGuardService implements CanActivate { | |||
export class RoutingGuardService implements CanActivate, OnInit { | |||
constructor(private userService:UserService, public router: Router) { | |||
constructor(private userService:UserService, public router: Router, private authService: AuthenticationService) { | |||
} | |||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { | |||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { | |||
const permString = route.data.perm; | |||
if (permString==null || permString=='') { | |||
console.error("Guard active, but permissions not set for route " + state.url); | |||
return false; | |||
} | |||
if (this.authService.authenticating) { | |||
console.debug("Guard: Authentication service is in authentication process"); | |||
return this.authService.LoginEvent.pipe(take(1),timeout(1000), map(()=>{ | |||
const myPerm = this.getPermission(permString); | |||
if (!myPerm) { | |||
this.router.navigate(['']); | |||
} | |||
return myPerm; | |||
})); | |||
} | |||
let perm = this.getPermission(permString); | |||
console.debug("Permission for " + state.url + ": " + perm); | |||
if (!perm) { | |||
this.router.navigate(['']); | |||
} | |||
return perm; | |||
} | |||
private getPermission(permString: string) { | |||
let perm = this.userService.uiPermissions; | |||
for (let permPath of permString.split(/\./)) { | |||
perm = perm[permPath]; | |||
@@ -48,10 +71,11 @@ export class RoutingGuardService implements CanActivate { | |||
break; | |||
} | |||
} | |||
console.debug("Permission for " + state.url + ": " + perm); | |||
if (!perm) { | |||
this.router.navigate(['']); | |||
} | |||
return perm; | |||
} | |||
ngOnInit(): void { | |||
} | |||
} | |||
@@ -88,7 +88,7 @@ export class UserService implements OnInit, OnDestroy { | |||
console.log("Could not retrieve permissions " + err); | |||
} | |||
} | |||
this.retrievePermissionInfo().subscribe(observer); | |||
this.retrievePermissionInfo("guest").subscribe(observer); | |||
} | |||
} | |||
@@ -138,34 +138,56 @@ export class UserService implements OnInit, OnDestroy { | |||
/** | |||
* 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.error_messages != null) { | |||
for (let msg of result.error_messages) { | |||
console.debug('Observer got an error: ' + msg.error_key) | |||
} | |||
public retrievePermissionInfo(userNameParam?:string): Observable<Permission[]> { | |||
let userName; | |||
if (userNameParam==null||userNameParam=='') { | |||
userName = this.authenticated ? "me" : "guest"; | |||
} else { | |||
userName = userNameParam; | |||
} | |||
return this.rest.executeRestCall<Permission[]>("get", "redback", "users/" + userName + "/permissions", null).pipe( | |||
catchError((err:HttpErrorResponse)=> { | |||
console.log("Error " + (JSON.stringify(err))); | |||
let result = err.error as ErrorResult | |||
if (result.error_messages != null) { | |||
for (let msg of result.error_messages) { | |||
console.debug('Observer got an error: ' + msg.error_key) | |||
} | |||
this.resetPermissions(); | |||
resultObserver.error(err); | |||
}, | |||
complete: () => { | |||
resultObserver.complete(); | |||
} | |||
}; | |||
infoObserver.subscribe(permissionObserver); | |||
this.resetPermissions(); | |||
return []; | |||
}), map((perm:Permission[])=>{ | |||
this.permissions = perm; | |||
this.parsePermissions(perm); | |||
return perm; | |||
}) | |||
); | |||
}); | |||
// return new Observable<Permission[]>((resultObserver) => { | |||
// 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.error_messages != null) { | |||
// for (let msg of result.error_messages) { | |||
// console.debug('Observer got an error: ' + msg.error_key) | |||
// } | |||
// } | |||
// this.resetPermissions(); | |||
// resultObserver.error(err); | |||
// }, | |||
// complete: () => { | |||
// resultObserver.complete(); | |||
// } | |||
// }; | |||
// infoObserver.subscribe(permissionObserver); | |||
// | |||
// }); | |||
} | |||
resetPermissions() { |