diff options
author | Martin Stockhammer <martin_s@apache.org> | 2020-11-13 16:39:08 +0100 |
---|---|---|
committer | Martin Stockhammer <martin_s@apache.org> | 2020-11-13 16:55:44 +0100 |
commit | 4a72e6f0dbb712a2aa43f8755a5cdfe24295e124 (patch) | |
tree | be6dfd0bdd29ac31b93e8d8e171e608b8d2af703 /archiva-modules | |
parent | 16005844ddf19066293203a292fad67a54e30dd9 (diff) | |
download | archiva-4a72e6f0dbb712a2aa43f8755a5cdfe24295e124.tar.gz archiva-4a72e6f0dbb712a2aa43f8755a5cdfe24295e124.zip |
Adding generic error handling
Diffstat (limited to 'archiva-modules')
13 files changed, 425 insertions, 6 deletions
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts index f4699f83d..15fcbd739 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts @@ -33,6 +33,12 @@ import {SecurityConfigurationComponent} from "./modules/user/security-configurat 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 {ManageUsersEditComponent} from "./modules/user/users/manage-users-edit/manage-users-edit.component"; +import {RoutingGuardService as Guard} from "./services/routing-guard.service"; + +/** + * You can use Guard (RoutingGuardService) for permission checking. The service needs data with one parameter 'perm', + * that gives the path of the uiPermission map of the user service. + */ const routes: Routes = [ { path: '', component: HomeComponent, @@ -42,9 +48,10 @@ const routes: Routes = [ {path:'repo/upload', component: UploadComponent}, {path:'', redirectTo:'repo/search', pathMatch:'full'}, ]}, - { path: 'user', component: HomeComponent, + { path: 'user', component: HomeComponent,canActivate:[Guard],data:{perm: 'menu.user.section'}, children: [ - { path: 'users', component: ManageUsersComponent, + { path: 'users', component: ManageUsersComponent,canActivate:[Guard], + data: { perm: 'menu.user.manage' }, children: [ {path: 'list', component: ManageUsersListComponent}, {path: 'add', component: ManageUsersAddComponent}, 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 7525842e9..e1d9bb0c9 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 @@ -16,6 +16,25 @@ ~ specific language governing permissions and limitations ~ under the License. --> +<ng-template #alertcontainer let-modal> + <div class="modal-header alert alert-danger"> + <h5 class="modal-title alert" id="modal-basic-title">{{'error.modal.title'|translate}}</h5> + <button type="button" class="close alert" aria-label="{{'modal.close'|translate}}" (click)="modal.dismiss('Cross click')"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body"> + <div class="alert alert-danger"> + <p>{{'error.modal.info'|translate}}</p> + </div> + <div *ngFor="let message of errorMessages" class="alert alert-secondary"> + <p>{{message.message}}</p> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-danger" (click)="modal.close('Save click')">{{'modal.close'|translate}}</button> + </div> +</ng-template> <div class="app d-flex flex-column"> <header> <nav class="navbar navbar-expand-md fixed-top navbar-light " style="background-color: #c6cbd2;"> 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 e88a6a1ce..01a1423f4 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 @@ -16,29 +16,45 @@ * specific language governing permissions and limitations * under the License. */ -import {Component, OnDestroy, OnInit} from '@angular/core'; +import {AfterViewInit, Component, OnDestroy, OnInit, ViewChild, ViewChildren} from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { AuthenticationService } from "./services/authentication.service"; import {UserService} from "./services/user.service"; +import {ErrorDialogService} from "./services/error/error-dialog.service"; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import {NgbModal} from "@ng-bootstrap/ng-bootstrap"; +import {ErrorMessage} from "./model/error-message"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent implements OnInit, OnDestroy{ +export class AppComponent implements OnInit, OnDestroy { title = 'archiva-web'; version = 'Angular version 10.0.2'; + @ViewChild('alertcontainer') errorAlert; + private alertUnsubscribe = new Subject(); + private errorOpen=false; + errorMessages: Array<ErrorMessage> = new Array<ErrorMessage>(); + + constructor( public translate: TranslateService, public auth: AuthenticationService, - public user: UserService + public user: UserService, + public error: ErrorDialogService, + private modalService: NgbModal ) { + translate.addLangs(['en', 'de']); translate.setDefaultLang('en'); + this.initializeErrors(); } + switchLang(lang: string) { this.translate.use(lang); this.user.userInfo.language = lang; @@ -58,6 +74,8 @@ export class AppComponent implements OnInit, OnDestroy{ ngOnDestroy(): void { this.auth.LoginEvent.unsubscribe(); + this.alertUnsubscribe.next(); + this.alertUnsubscribe.complete(); } @@ -76,4 +94,27 @@ export class AppComponent implements OnInit, OnDestroy{ }) } + + + private initializeErrors() + { + this + .error + .getErrors() + .pipe(takeUntil(this.alertUnsubscribe)) + .subscribe((errorMsg) => + { + this.errorMessages.push(errorMsg); + if (!this.errorOpen) { + this.errorOpen=true; + this.modalService.open(this.errorAlert).result.then((result) => { + this.errorOpen=false; + this.errorMessages.length = 0; + }, (reason) => { + this.errorOpen=false; + this.errorMessages.length = 0; + }); + } + }); + } } 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 bd46271ec..d07e42f06 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 @@ -47,6 +47,8 @@ import { PaginatedEntitiesComponent } from './modules/general/paginated-entities 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 {CoreModule} from "./modules/core/core.module"; @NgModule({ @@ -91,7 +93,8 @@ import { ManageUsersEditComponent } from './modules/user/users/manage-users-edit } }), NgbPaginationModule, - NgbTooltipModule + NgbTooltipModule, + CoreModule ], providers: [ { provide: MESSAGE_FORMAT_CONFIG, useValue: { locales: ['en', 'de'] }} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/core.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/core.module.ts new file mode 100644 index 000000000..28309c27d --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/core.module.ts @@ -0,0 +1,32 @@ +/* + * 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 {ErrorHandlerModule} from "./errors/error-handler.module"; + + + +@NgModule({ + declarations: [], + imports: [ + CommonModule, + ErrorHandlerModule + ] +}) +export class CoreModule { } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/errors/error-handler.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/errors/error-handler.module.ts new file mode 100644 index 000000000..12e16defc --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/errors/error-handler.module.ts @@ -0,0 +1,37 @@ +/* + * 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, ErrorHandler } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { GlobalErrorHandler } from './global-error-handler'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { HttpErrorInterceptor } from './http-error-interceptor'; + + + +@NgModule({ + declarations: [], + imports: [ + CommonModule, + ], + providers: [ + { provide: ErrorHandler, useClass: GlobalErrorHandler }, + { provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true } + ] +}) +export class ErrorHandlerModule { } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/errors/global-error-handler.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/errors/global-error-handler.ts new file mode 100644 index 000000000..a4478c706 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/errors/global-error-handler.ts @@ -0,0 +1,32 @@ +/* + * 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 {ErrorHandler, Injectable} from "@angular/core"; + +@Injectable() +export class GlobalErrorHandler implements ErrorHandler{ + handleError(error: any): void { + try { + console.log("GlobalErrorHandler catched: " + error + " - " + typeof (error)); + console.log("JSON "+JSON.stringify(error)); + } catch (e) { + console.error("Could not log: " + e); + // + }; + } +} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/errors/http-error-interceptor.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/errors/http-error-interceptor.ts new file mode 100644 index 000000000..a4ebbd5a8 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/errors/http-error-interceptor.ts @@ -0,0 +1,58 @@ +/* + * 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 { + HttpHandler, + HttpRequest, + HttpEvent, + HttpErrorResponse, + HttpInterceptor +} from "@angular/common/http"; +import { Observable, throwError } from "rxjs"; +import { catchError, finalize } from "rxjs/operators"; +import { Injectable } from "@angular/core"; +import {ErrorDialogService} from "../../../services/error/error-dialog.service"; + +/** + * Checks for generic HTTP errors and adds messages to the error service. + */ +@Injectable() +export class HttpErrorInterceptor implements HttpInterceptor { + constructor(private errorService: ErrorDialogService) {} + + intercept( + request: HttpRequest<any>, + next: HttpHandler + ): Observable<HttpEvent<any>> { + return next.handle(request).pipe( + catchError((error: HttpErrorResponse) => { + console.error("Error from HTTP error interceptor", error); + if (error.status==0 && error.statusText=="Unknown Error") { + console.log("Unknown error"); + this.errorService.addError('error.http.unknownError'); + } else if (error.status==403) { + console.log("Permission error "+error.message); + this.errorService.addError('error.http.permissionDenied'); + } + return throwError(error); + }), + finalize(() => { + }) + ) as Observable<HttpEvent<any>>; + } +}
\ No newline at end of file diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/error/error-dialog.service.spec.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/error/error-dialog.service.spec.ts new file mode 100644 index 000000000..51cf06365 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/error/error-dialog.service.spec.ts @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { TestBed } from '@angular/core/testing'; + +import { ErrorDialogService } from './error-dialog.service'; + +describe('ErrorDialogService', () => { + let service: ErrorDialogService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ErrorDialogService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/error/error-dialog.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/error/error-dialog.service.ts new file mode 100644 index 000000000..e312709e5 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/error/error-dialog.service.ts @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {Injectable} from '@angular/core'; +import {ErrorMessage} from "../../model/error-message"; +import { Router } from '@angular/router'; +import { Subject } from 'rxjs'; +import {TranslateService} from "@ngx-translate/core"; + +@Injectable({ + providedIn: 'root' +}) +export class ErrorDialogService { + + private errors= new Subject<ErrorMessage>(); + private opened = false; + + constructor(private router : Router, private translate : TranslateService) {} + + + public addError(messageKey:string, args?:string[]) { + let msg = new ErrorMessage(); + msg.error_key = messageKey; + msg.args = args; + if (msg.message==null||msg.message=='') { + msg.message = this.translate.instant(msg.error_key, msg.args); + } + this.errors.next(msg); + } + + + public getErrors = () => + this.errors.asObservable(); + + + public showError() { + + this.router.navigate(['error'], { queryParams:{'dialog':true} } ); + } +} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/routing-guard.service.spec.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/routing-guard.service.spec.ts new file mode 100644 index 000000000..9545ac346 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/routing-guard.service.spec.ts @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { TestBed } from '@angular/core/testing'; + +import { RoutingGuardService } from './routing-guard.service'; + +describe('RoutingGuardService', () => { + let service: RoutingGuardService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(RoutingGuardService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/routing-guard.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/routing-guard.service.ts new file mode 100644 index 000000000..0e7a07961 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/routing-guard.service.ts @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Injectable } from '@angular/core'; +import {UserService} from "./user.service"; +import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router} from "@angular/router"; + +/** + * Guard for the routes, that checks permissions by querying the uiPermission map of the UserService. + * The guard checks the data in the routing definition for a 'perm' entry and uses this as path for the + * uiPermission map. + */ +@Injectable({ + providedIn: 'root' +}) +export class RoutingGuardService implements CanActivate { + + constructor(private userService:UserService, public router: Router) { + + } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + const permString = route.data.perm; + if (permString==null || permString=='') { + console.error("Guard active, but permissions not set for route " + state.url); + return false; + } + let perm = this.userService.uiPermissions; + for (let permPath of permString.split(/\./)) { + perm = perm[permPath]; + if (perm==null) { + perm=false; + break; + } + } + console.debug("Permission for " + state.url + ": " + perm); + if (!perm) { + this.router.navigate(['']); + } + return perm; + } +} diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json index aaffa09e4..a94ad5405 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json @@ -16,6 +16,16 @@ "about": "About", "contact": "Contact" }, + "error": { + "modal": { + "title": "Application Error", + "info":"The backend does not answer as expected. Please check, if your archiva service is running. See detail messages below." + }, + "http": { + "unknownError": "We got a bad response from the backend REST service. Maybe the connection is broken, or the service is down. Please check your network to the backend service and the archiva.log for any errors.", + "permissionDenied": "You are not allowed to access the data from the backend service. Try to login again and/or check your permissions." + } + }, "sidemenu": { "repo": { "section": "Artifacts", |