mirror of https://github.com/apache/archiva.git
Additional angular code
This commit is contained in:
parent
108b60a4df
commit
fb2a7fd643
|
@ -30,6 +30,7 @@ const routes: Routes = [
|
||||||
{ path: 'contact', component: ContactComponent },
|
{ path: 'contact', component: ContactComponent },
|
||||||
{ path: 'about', component: AboutComponent },
|
{ path: 'about', component: AboutComponent },
|
||||||
{ path: 'login', component: LoginComponent },
|
{ path: 'login', component: LoginComponent },
|
||||||
|
{ path: 'logout', component: HomeComponent },
|
||||||
{ path: '**', component: NotFoundComponent }
|
{ path: '**', component: NotFoundComponent }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -16,70 +16,84 @@
|
||||||
~ specific language governing permissions and limitations
|
~ specific language governing permissions and limitations
|
||||||
~ under the License.
|
~ under the License.
|
||||||
-->
|
-->
|
||||||
<div class="app d-flex flex-column" >
|
<div class="app d-flex flex-column">
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-expand-md fixed-top navbar-light " style="background-color: #c6cbd2;">
|
<nav class="navbar navbar-expand-md fixed-top navbar-light " style="background-color: #c6cbd2;">
|
||||||
<a class="navbar-brand" routerLink="/">
|
<a class="navbar-brand" routerLink="/">
|
||||||
<img src="../assets/params/images/archiva_logo_40h.png" alt="ARCHIVA">
|
<img src="../assets/params/images/archiva_logo_40h.png" alt="ARCHIVA">
|
||||||
</a>
|
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsDefault"
|
|
||||||
aria-controls="navbarsDefault" aria-expanded="false" aria-label="Toggle Navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navbarsDefault">
|
|
||||||
<ul class="navbar-nav ml-auto">
|
|
||||||
<li class="nav-item active">
|
|
||||||
<a class="nav-link" routerLink="/">
|
|
||||||
<i class="fas fa-home mr-1"></i>{{ 'menu.home' |translate }}
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsDefault"
|
||||||
<li class="nav-item active">
|
aria-controls="navbarsDefault" aria-expanded="false" aria-label="Toggle Navigation">
|
||||||
<a class="nav-link" routerLink="/login" data-toggle="modal" data-target="#loginModal">
|
<span class="navbar-toggler-icon"></span>
|
||||||
<i class="fas fa-user mr-1"></i>{{'menu.login' | translate}}
|
</button>
|
||||||
</a>
|
|
||||||
</li>
|
<div class="collapse navbar-collapse" id="navbarsDefault">
|
||||||
<li class="nav-item active dropdown">
|
<div class="navbar-nav ml-auto">
|
||||||
<a class="nav-link dropdown-toggle" id="dropdown09" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="flag-icon {{langIcon()}}"></span></a>
|
<span *ngIf="auth.loggedIn" class="navbar-text border-right pr-2 mr-2">
|
||||||
<div class="dropdown-menu" aria-labelledby="dropdown09">
|
{{user.userInfo.fullName}}
|
||||||
<a class="dropdown-item" href="#en" (click)="switchLang('en')"><span class="flag-icon flag-icon-gb"> </span> English</a>
|
</span>
|
||||||
<a class="dropdown-item" href="#de" (click)="switchLang('de')"><span class="flag-icon flag-icon-de"> </span> German</a>
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" routerLink="/">
|
||||||
|
<i class="fas fa-home mr-1"></i>{{ 'menu.home' |translate }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="!auth.loggedIn" 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">
|
||||||
|
<a class="nav-link" routerLink="/logout" (click)="auth.logout()">
|
||||||
|
<i class="fas fa-user mr-1"></i>{{'menu.logout' | translate}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item active dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" id="dropdown09" data-toggle="dropdown"
|
||||||
|
aria-haspopup="true" aria-expanded="false"><span class="flag-icon {{langIcon()}}"></span></a>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="dropdown09">
|
||||||
|
<a class="dropdown-item" href="#en" (click)="switchLang('en')"><span
|
||||||
|
class="flag-icon flag-icon-gb"> </span> English</a>
|
||||||
|
<a class="dropdown-item" href="#de" (click)="switchLang('de')"><span
|
||||||
|
class="flag-icon flag-icon-de"> </span> German</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" routerLink="/about">
|
||||||
|
<i class="far fa-question-circle mr-1"></i>{{'menu.about' | translate}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" routerLink="/contact">
|
||||||
|
<i class="fas fa-envelope mr-1"></i>{{ 'menu.contact' | translate }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" href="https://github.com/apache/archiva">
|
||||||
|
<i class="fab fa-github mr-1"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</nav>
|
||||||
<li class="nav-item active">
|
</header>
|
||||||
<a class="nav-link" routerLink="/about">
|
|
||||||
<i class="far fa-question-circle mr-1"></i>{{'menu.about' | translate}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item active">
|
|
||||||
<a class="nav-link" routerLink="/contact">
|
|
||||||
<i class="fas fa-envelope mr-1"></i>{{ 'menu.contact' | translate }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item active">
|
|
||||||
<a class="nav-link" href="https://github.com/apache/archiva">
|
|
||||||
<i class="fab fa-github mr-1"></i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main class="container-fluid flex-fill">
|
<main class="container-fluid flex-fill">
|
||||||
<div >
|
<div>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<footer class="container-fluid">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
<p class="text-center text-black-50">© 2020 - archiva.apache.org</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
</div>
|
|
||||||
</footer>
|
<hr/>
|
||||||
|
<footer class="container-fluid">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<p class="text-center text-black-50">© 2020 - archiva.apache.org</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
</div>
|
</div>
|
|
@ -16,28 +16,33 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { Component } from '@angular/core';
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { AuthenticationService } from "./services/authentication.service";
|
||||||
|
import {UserService} from "./services/user.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.scss']
|
styleUrls: ['./app.component.scss']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent implements OnInit, OnDestroy{
|
||||||
title = 'archiva-web';
|
title = 'archiva-web';
|
||||||
version = 'Angular version 10.0.2';
|
version = 'Angular version 10.0.2';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public translate: TranslateService
|
public translate: TranslateService,
|
||||||
|
public auth: AuthenticationService,
|
||||||
|
public user: UserService
|
||||||
) {
|
) {
|
||||||
translate.addLangs(['en', 'de']);
|
translate.addLangs(['en', 'de']);
|
||||||
translate.setDefaultLang('en');
|
translate.setDefaultLang('en');
|
||||||
translate.use('en');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switchLang(lang: string) {
|
switchLang(lang: string) {
|
||||||
this.translate.use(lang);
|
this.translate.use(lang);
|
||||||
|
this.user.userInfo.language = lang;
|
||||||
|
this.user.persistUserInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
langIcon() : string {
|
langIcon() : string {
|
||||||
|
@ -50,4 +55,25 @@ export class AppComponent {
|
||||||
return "flag-icon-" + this.translate.currentLang;
|
return "flag-icon-" + this.translate.currentLang;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.auth.LoginEvent.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
let lang = this.user.userInfo.language;
|
||||||
|
if (lang==null) {
|
||||||
|
this.translate.use('en');
|
||||||
|
} else {
|
||||||
|
this.translate.use(lang);
|
||||||
|
}
|
||||||
|
// Subscribe to login event in authenticator to switch the language
|
||||||
|
this.auth.LoginEvent.subscribe(userInfo => {
|
||||||
|
if (userInfo.language != null) {
|
||||||
|
this.switchLang(userInfo.language);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { UserInfo } from './user-info';
|
||||||
|
|
||||||
|
describe('UserInfo', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new UserInfo()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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 UserInfo {
|
||||||
|
user_id:string;
|
||||||
|
id:string;
|
||||||
|
fullName:string;
|
||||||
|
email:string;
|
||||||
|
validated:boolean;
|
||||||
|
locked:boolean;
|
||||||
|
passwordChangeRequired:boolean;
|
||||||
|
permanent:boolean;
|
||||||
|
timestampAccountCreation:Date;
|
||||||
|
timestampLastLogin:Date;
|
||||||
|
timestampLastPasswordChange:Date;
|
||||||
|
assignedRoles:string[];
|
||||||
|
readOnly:boolean;
|
||||||
|
userManagerId:string;
|
||||||
|
validationToken:string;
|
||||||
|
language:string;
|
||||||
|
}
|
|
@ -74,4 +74,7 @@ export class LoginComponent implements OnInit {
|
||||||
this.loginForm.reset();
|
this.loginForm.reset();
|
||||||
this.authenticationService.login(customerData.userid, customerData.password, resultHandler);
|
this.authenticationService.login(customerData.userid, customerData.password, resultHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,49 +16,81 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { Injectable } from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {HttpClient, HttpEvent, HttpResponse} from "@angular/common/http";
|
import {HttpClient} from "@angular/common/http";
|
||||||
import { environment } from "../../environments/environment";
|
import {environment} from "../../environments/environment";
|
||||||
import {Observable} from "rxjs";
|
import {Observable} from "rxjs";
|
||||||
import {ErrorMessage} from "../model/error-message";
|
import {ErrorMessage} from "../model/error-message";
|
||||||
import {TranslateService} from "@ngx-translate/core";
|
import {TranslateService} from "@ngx-translate/core";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ArchivaRequestService {
|
export class ArchivaRequestService {
|
||||||
|
|
||||||
constructor(private http : HttpClient, private translator : TranslateService) { }
|
// Stores the access token locally
|
||||||
|
accessToken: string;
|
||||||
|
|
||||||
executeRestCall<R>(type: string, module: string, service: string, input: object ) : Observable<R> {
|
constructor(private http: HttpClient, private translator: TranslateService) {
|
||||||
let modulePath = environment.application.servicePaths[module];
|
|
||||||
let url = environment.application.baseUrl + environment.application.restPath + "/" + modulePath + "/" + service;
|
|
||||||
let token = localStorage.getItem("access_token")
|
|
||||||
let headers = null;
|
|
||||||
if (token != null) {
|
|
||||||
headers = {
|
|
||||||
"Authorization": "Bearer " + localStorage.getItem("access_token")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
headers = {};
|
|
||||||
}
|
}
|
||||||
if (type == "get") {
|
|
||||||
return this.http.get<R>(url, {"headers":headers});
|
|
||||||
} else if ( type == "post") {
|
|
||||||
return this.http.post<R>(url, input, {"headers":headers});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
translateError(errorMsg : ErrorMessage) : string {
|
* Executes a rest call to the archiva / redback REST services.
|
||||||
if (errorMsg.errorKey!=null && errorMsg.errorKey!='') {
|
* @param type the type of the call (get, post, update)
|
||||||
let parms = {};
|
* @param module the module (archiva, redback)
|
||||||
if (errorMsg.args!=null && errorMsg.args.length>0) {
|
* @param service the REST service to call
|
||||||
for ( let i=0; i<errorMsg.args.length; i++) {
|
* @param input the input data, if this is a POST or UPDATE request
|
||||||
parms['arg' + i] = errorMsg.args[i];
|
*/
|
||||||
|
executeRestCall<R>(type: string, module: string, service: string, input: object): Observable<R> {
|
||||||
|
let modulePath = environment.application.servicePaths[module];
|
||||||
|
let url = environment.application.baseUrl + environment.application.restPath + "/" + modulePath + "/" + service;
|
||||||
|
let token = this.getToken();
|
||||||
|
let headers = null;
|
||||||
|
if (token != null) {
|
||||||
|
headers = {
|
||||||
|
"Authorization": "Bearer " + token
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
headers = {};
|
||||||
|
}
|
||||||
|
if (type == "get") {
|
||||||
|
return this.http.get<R>(url, {"headers": headers});
|
||||||
|
} else if (type == "post") {
|
||||||
|
return this.http.post<R>(url, input, {"headers": headers});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetToken() {
|
||||||
|
this.accessToken = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getToken(): string {
|
||||||
|
if (this.accessToken != null) {
|
||||||
|
return this.accessToken;
|
||||||
|
} else {
|
||||||
|
let token = localStorage.getItem("access_token");
|
||||||
|
if (token != null && token != "") {
|
||||||
|
this.accessToken = token;
|
||||||
|
return token;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates a given error message to the current set language.
|
||||||
|
* @param errorMsg the errorMsg as returned by a REST call
|
||||||
|
*/
|
||||||
|
public translateError(errorMsg: ErrorMessage): string {
|
||||||
|
if (errorMsg.errorKey != null && errorMsg.errorKey != '') {
|
||||||
|
let parms = {};
|
||||||
|
if (errorMsg.args != null && errorMsg.args.length > 0) {
|
||||||
|
for (let i = 0; i < errorMsg.args.length; i++) {
|
||||||
|
parms['arg' + i] = errorMsg.args[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.translator.instant('api.' + errorMsg.errorKey, parms);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return this.translator.instant('api.'+errorMsg.errorKey, parms);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,61 +16,117 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { Injectable } from '@angular/core';
|
import {EventEmitter, Injectable} from '@angular/core';
|
||||||
import {ArchivaRequestService} from "./archiva-request.service";
|
import {ArchivaRequestService} from "./archiva-request.service";
|
||||||
import {AccessToken} from "../model/access-token";
|
import {AccessToken} from "../model/access-token";
|
||||||
import { environment } from "../../environments/environment";
|
import {environment} from "../../environments/environment";
|
||||||
import {ErrorMessage} from "../model/error-message";
|
import {ErrorMessage} from "../model/error-message";
|
||||||
import {ErrorResult} from "../model/error-result";
|
import {ErrorResult} from "../model/error-result";
|
||||||
import {HttpErrorResponse} from "@angular/common/http";
|
import {HttpErrorResponse} from "@angular/common/http";
|
||||||
|
import {UserService} from "./user.service";
|
||||||
|
import {UserInfo} from "../model/user-info";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AuthenticationService handles user authentication and stores user data after successful login
|
||||||
|
*/
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AuthenticationService {
|
export class AuthenticationService {
|
||||||
|
loggedIn: boolean;
|
||||||
|
|
||||||
constructor(private rest: ArchivaRequestService) { }
|
/**
|
||||||
|
* The LoginEvent is emitted, when a successful login happened. And the corresponding user info was retrieved.
|
||||||
|
*/
|
||||||
|
public LoginEvent: EventEmitter<UserInfo> = new EventEmitter<UserInfo>();
|
||||||
|
|
||||||
login(userid:string, password:string, resultHandler: (n: string, err?: ErrorMessage[]) => void) {
|
|
||||||
|
|
||||||
const data = { 'grant_type':'authorization_code',
|
constructor(private rest: ArchivaRequestService,
|
||||||
'client_id':environment.application.client_id,
|
private userService: UserService) {
|
||||||
'user_id':userid, 'password':password
|
this.loggedIn = false;
|
||||||
};
|
this.restoreLoginData();
|
||||||
let authObserver = this.rest.executeRestCall<AccessToken>('post','redback', 'auth/authenticate', data );
|
}
|
||||||
let tokenObserver = {
|
|
||||||
next: (x: AccessToken) => {
|
|
||||||
localStorage.setItem("access_token", x.access_token);
|
|
||||||
localStorage.setItem("refresh_token", x.refresh_token);
|
|
||||||
if (x.expires_in!=null) {
|
|
||||||
let dt = new Date();
|
|
||||||
dt.setSeconds(dt.getSeconds() + x.expires_in);
|
|
||||||
localStorage.setItem("token_expire", dt.toISOString());
|
|
||||||
}
|
|
||||||
resultHandler("OK");
|
|
||||||
},
|
|
||||||
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.error('Observer got an error: ' + msg.errorKey)
|
|
||||||
}
|
|
||||||
resultHandler("ERROR", result.errorMessages);
|
|
||||||
} else {
|
|
||||||
resultHandler("ERROR", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
private restoreLoginData() {
|
||||||
// complete: () => console.log('Observer got a complete notification'),
|
let accessToken = localStorage.getItem("access_token");
|
||||||
};
|
if (accessToken != null) {
|
||||||
authObserver.subscribe(tokenObserver)
|
let expirationDate = localStorage.getItem("token_expire");
|
||||||
|
if (expirationDate != null) {
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
logout() {
|
}
|
||||||
localStorage.removeItem("access_token");
|
|
||||||
localStorage.removeItem("refresh_token");
|
|
||||||
localStorage.removeItem("token_expire");
|
/**
|
||||||
}
|
* Tries to login by sending the login data to the REST service. If the login was successful the access
|
||||||
|
* and refresh token is stored locally.
|
||||||
|
*
|
||||||
|
* @param userid The user id for the login
|
||||||
|
* @param password The password
|
||||||
|
* @param resultHandler A result handler that is executed, after calling the login service
|
||||||
|
*/
|
||||||
|
login(userid: string, password: string, resultHandler: (n: string, err?: ErrorMessage[]) => void) {
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
'grant_type': 'authorization_code',
|
||||||
|
'client_id': environment.application.client_id,
|
||||||
|
'user_id': userid, 'password': password
|
||||||
|
};
|
||||||
|
let authObserver = this.rest.executeRestCall<AccessToken>('post', 'redback', 'auth/authenticate', data);
|
||||||
|
let tokenObserver = {
|
||||||
|
next: (x: AccessToken) => {
|
||||||
|
localStorage.setItem("access_token", x.access_token);
|
||||||
|
localStorage.setItem("refresh_token", x.refresh_token);
|
||||||
|
if (x.expires_in != null) {
|
||||||
|
let dt = new Date();
|
||||||
|
dt.setSeconds(dt.getSeconds() + x.expires_in);
|
||||||
|
localStorage.setItem("token_expire", dt.toISOString());
|
||||||
|
}
|
||||||
|
let userObserver = this.userService.retrieveUserInfo();
|
||||||
|
this.loggedIn = true;
|
||||||
|
userObserver.subscribe(userInfo =>
|
||||||
|
this.LoginEvent.emit(userInfo));
|
||||||
|
resultHandler("OK");
|
||||||
|
},
|
||||||
|
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.error('Observer got an error: ' + msg.errorKey)
|
||||||
|
}
|
||||||
|
resultHandler("ERROR", result.errorMessages);
|
||||||
|
} else {
|
||||||
|
resultHandler("ERROR", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
// complete: () => console.log('Observer got a complete notification'),
|
||||||
|
};
|
||||||
|
authObserver.subscribe(tokenObserver)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the stored user data
|
||||||
|
*/
|
||||||
|
logout() {
|
||||||
|
localStorage.removeItem("access_token");
|
||||||
|
localStorage.removeItem("refresh_token");
|
||||||
|
localStorage.removeItem("token_expire");
|
||||||
|
this.loggedIn = false;
|
||||||
|
this.userService.resetUser();
|
||||||
|
this.rest.resetToken();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { UserService } from './user.service';
|
||||||
|
|
||||||
|
describe('UserService', () => {
|
||||||
|
let service: UserService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(UserService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {ArchivaRequestService} from "./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";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class UserService {
|
||||||
|
|
||||||
|
userInfo: UserInfo;
|
||||||
|
|
||||||
|
constructor(private rest: ArchivaRequestService) {
|
||||||
|
this.userInfo = new UserInfo()
|
||||||
|
this.loadPersistedUserInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the user information from the REST service for the current logged in user.
|
||||||
|
* This works only, if a valid access token is present.
|
||||||
|
* It returns a observable that can be subscribed to catch the user information.
|
||||||
|
*/
|
||||||
|
public retrieveUserInfo(): Observable<UserInfo> {
|
||||||
|
return new Observable<UserInfo>((resultObserver) => {
|
||||||
|
let accessToken = localStorage.getItem("access_token");
|
||||||
|
|
||||||
|
if (accessToken != null) {
|
||||||
|
let infoObserver = this.rest.executeRestCall<UserInfo>("get", "redback", "users/me", null);
|
||||||
|
let userInfoObserver = {
|
||||||
|
next: (x: UserInfo) => {
|
||||||
|
this.userInfo = x;
|
||||||
|
if (this.userInfo.language == null) {
|
||||||
|
this.loadPersistedUserInfo();
|
||||||
|
}
|
||||||
|
this.persistUserInfo();
|
||||||
|
resultObserver.next(this.userInfo);
|
||||||
|
},
|
||||||
|
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.error('Observer got an error: ' + msg.errorKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultObserver.error();
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
resultObserver.complete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
infoObserver.subscribe(userInfoObserver);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores user information persistent. Not the complete UserInfo object, only properties, that
|
||||||
|
* are needed.
|
||||||
|
*/
|
||||||
|
public persistUserInfo() {
|
||||||
|
if (this.userInfo != null && this.userInfo.user_id != null && this.userInfo.user_id != "") {
|
||||||
|
let prefix = "user." + this.userInfo.user_id;
|
||||||
|
localStorage.setItem(prefix + ".user_id", this.userInfo.user_id);
|
||||||
|
localStorage.setItem(prefix + ".id", this.userInfo.id);
|
||||||
|
if (this.userInfo.language != null && this.userInfo.language != "") {
|
||||||
|
localStorage.setItem(prefix + ".language", this.userInfo.language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the persisted user info from the local storage
|
||||||
|
*/
|
||||||
|
public loadPersistedUserInfo() {
|
||||||
|
if (this.userInfo.user_id != null && this.userInfo.user_id != "") {
|
||||||
|
let prefix = "user." + this.userInfo.user_id;
|
||||||
|
this.userInfo.language = localStorage.getItem(prefix + ".language");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the user info to default values.
|
||||||
|
*/
|
||||||
|
resetUser() {
|
||||||
|
this.userInfo = new UserInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
"menu": {
|
"menu": {
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
|
"logout": "Abmelden",
|
||||||
"about": "Über",
|
"about": "Über",
|
||||||
"contact": "Kontakt"
|
"contact": "Kontakt"
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"menu": {
|
"menu": {
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
|
"logout": "Logout",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"contact": "Contact"
|
"contact": "Contact"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue