Maurizio Vitale 419fc166c4
Do redirect before implicit button (#6574)
* Do redirect before implicit

* Add unit to implicit and silent

* Keep the value as it was before
2021-01-25 17:06:35 +00:00

411 lines
12 KiB
TypeScript

/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed 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 {
Component, EventEmitter,
Input, OnInit, Output, TemplateRef, ViewEncapsulation, OnDestroy
} from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { AuthenticationService } from '../../services/authentication.service';
import { LogService } from '../../services/log.service';
import { TranslationService } from '../../services/translation.service';
import { UserPreferencesService } from '../../services/user-preferences.service';
import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { LoginErrorEvent } from '../models/login-error.event';
import { LoginSubmitEvent } from '../models/login-submit.event';
import { LoginSuccessEvent } from '../models/login-success.event';
import {
AppConfigService,
AppConfigValues
} from '../../app-config/app-config.service';
import { OauthConfigModel } from '../../models/oauth-config.model';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
enum LoginSteps {
Landing = 0,
Checking = 1,
Welcome = 2
}
interface ValidationMessage {
value: string;
params?: any;
}
@Component({
selector: 'adf-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
encapsulation: ViewEncapsulation.None,
host: {
class: 'adf-login'
}
})
export class LoginComponent implements OnInit, OnDestroy {
isPasswordShow: boolean = false;
/**
* Should the `Remember me` checkbox be shown? When selected, this
* option will remember the logged-in user after the browser is closed
* to avoid logging in repeatedly.
*/
@Input()
showRememberMe: boolean = true;
/** Should the extra actions (`Need Help`, `Register`, etc) be shown? */
@Input()
showLoginActions: boolean = true;
/** Sets the URL of the NEED HELP link in the footer. */
@Input()
needHelpLink: string = '';
/** Sets the URL of the REGISTER link in the footer. */
@Input()
registerLink: string = '';
/** Path to a custom logo image. */
@Input()
logoImageUrl: string = './assets/images/alfresco-logo.svg';
/** Path to a custom background image. */
@Input()
backgroundImageUrl: string = './assets/images/background.svg';
/** The copyright text below the login box. */
@Input()
copyrightText: string = '\u00A9 2016 Alfresco Software, Inc. All Rights Reserved.';
/** Custom validation rules for the login form. */
@Input()
fieldsValidation: any;
/** Route to redirect to on successful login. */
@Input()
successRoute: string = null;
/** Emitted when the login is successful. */
@Output()
success = new EventEmitter<LoginSuccessEvent>();
/** Emitted when the login fails. */
@Output()
error = new EventEmitter<LoginErrorEvent>();
/** Emitted when the login form is submitted. */
@Output()
executeSubmit = new EventEmitter<LoginSubmitEvent>();
implicitFlow: boolean = false;
form: FormGroup;
isError: boolean = false;
errorMsg: string;
actualLoginStep: any = LoginSteps.Landing;
LoginSteps = LoginSteps;
rememberMe: boolean = true;
formError: { [id: string]: string };
minLength: number = 2;
footerTemplate: TemplateRef<any>;
headerTemplate: TemplateRef<any>;
data: any;
private _message: { [id: string]: { [id: string]: ValidationMessage } };
private onDestroy$ = new Subject<boolean>();
constructor(
private _fb: FormBuilder,
private authService: AuthenticationService,
private translateService: TranslationService,
private logService: LogService,
private router: Router,
private appConfig: AppConfigService,
private userPreferences: UserPreferencesService,
private route: ActivatedRoute,
private sanitizer: DomSanitizer,
private alfrescoApiService: AlfrescoApiService
) {
}
ngOnInit() {
this.initFormError();
this.initFormFieldsDefault();
this.initFormFieldsMessages();
if (this.authService.isLoggedIn()) {
this.router.navigate([this.successRoute]);
} else {
if (this.authService.isOauth()) {
const oauth: OauthConfigModel = this.appConfig.get<OauthConfigModel>(AppConfigValues.OAUTHCONFIG, null);
if (oauth && oauth.silentLogin) {
this.redirectToImplicitLogin();
} else if (oauth && oauth.implicitFlow) {
this.implicitFlow = true;
}
}
this.route.queryParams.subscribe((params: Params) => {
const url = params['redirectUrl'];
const provider = this.appConfig.get<string>(AppConfigValues.PROVIDERS);
this.authService.setRedirect({ provider, url });
});
}
if (this.fieldsValidation) {
this.form = this._fb.group(this.fieldsValidation);
}
this.form.valueChanges
.pipe(takeUntil(this.onDestroy$))
.subscribe(data => this.onValueChanged(data));
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
submit() {
this.onSubmit(this.form.value);
}
redirectToImplicitLogin() {
this.alfrescoApiService.getInstance().oauth2Auth.implicitLogin();
}
/**
* Method called on submit form
* @param values
* @param event
*/
onSubmit(values: any): void {
this.disableError();
const args = new LoginSubmitEvent({
controls: { username: this.form.controls.username }
});
this.executeSubmit.emit(args);
if (!args.defaultPrevented) {
this.performLogin(values);
}
}
implicitLogin() {
if (this.authService.isLoggedIn()) {
this.router.navigate([this.successRoute]);
}
this.authService.ssoImplicitLogin();
}
/**
* The method check the error in the form and push the error in the formError object
* @param data
*/
onValueChanged(data: any) {
this.disableError();
for (const field in this.formError) {
if (field) {
this.formError[field] = '';
const hasError =
(this.form.controls[field].errors && data[field] !== '') ||
(this.form.controls[field].dirty &&
!this.form.controls[field].valid);
if (hasError) {
for (const key in this.form.controls[field].errors) {
if (key) {
const message = this._message[field][key];
if (message && message.value) {
const translated = this.translateService.instant(message.value, message.params);
this.formError[field] += translated;
}
}
}
}
}
}
}
private performLogin(values: any) {
this.actualLoginStep = LoginSteps.Checking;
this.authService
.login(values.username, values.password, this.rememberMe)
.subscribe(
(token: any) => {
const redirectUrl = this.authService.getRedirect();
this.actualLoginStep = LoginSteps.Welcome;
this.userPreferences.setStoragePrefix(values.username);
values.password = null;
this.success.emit(
new LoginSuccessEvent(token, values.username, null)
);
if (redirectUrl) {
this.authService.setRedirect(null);
this.router.navigateByUrl(redirectUrl);
} else if (this.successRoute) {
this.router.navigate([this.successRoute]);
}
},
(err: any) => {
this.actualLoginStep = LoginSteps.Landing;
this.displayErrorMessage(err);
this.isError = true;
this.error.emit(new LoginErrorEvent(err));
},
() => this.logService.info('Login done')
);
}
/**
* Check and display the right error message in the UI
*/
private displayErrorMessage(err: any): void {
if (
err.error &&
err.error.crossDomain &&
err.error.message.indexOf('Access-Control-Allow-Origin') !== -1
) {
this.errorMsg = err.error.message;
} else if (
err.status === 403 &&
err.message.indexOf('Invalid CSRF-token') !== -1
) {
this.errorMsg = 'LOGIN.MESSAGES.LOGIN-ERROR-CSRF';
} else if (
err.status === 403 &&
err.message.indexOf('The system is currently in read-only mode') !==
-1
) {
this.errorMsg = 'LOGIN.MESSAGES.LOGIN-ECM-LICENSE';
} else {
this.errorMsg = 'LOGIN.MESSAGES.LOGIN-ERROR-CREDENTIALS';
}
}
/**
* Add a custom form error for a field
* @param field
* @param msg
*/
public addCustomFormError(field: string, msg: string) {
this.formError[field] += msg;
}
/**
* Add a custom validation rule error for a field
* @param field
* @param ruleId - i.e. required | minlength | maxlength
* @param msg
*/
addCustomValidationError(
field: string,
ruleId: string,
msg: string,
params?: any
) {
this._message[field][ruleId] = {
value: msg,
params
};
}
/**
* Display and hide the password value.
*/
toggleShowPassword(event: MouseEvent | KeyboardEvent) {
event.stopPropagation();
this.isPasswordShow = !this.isPasswordShow;
}
/**
* The method return if a field is valid or not
* @param field
*/
isErrorStyle(field: AbstractControl) {
return !field.valid && field.dirty && !field.pristine;
}
/**
* Trim username
*/
trimUsername(event: any) {
event.target.value = event.target.value.trim();
}
getBackgroundUrlImageUrl(): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(`url(${this.backgroundImageUrl})`);
}
/**
* Default formError values
*/
private initFormError() {
this.formError = {
username: '',
password: ''
};
}
/**
* Init form fields messages
*/
private initFormFieldsMessages() {
this._message = {
username: {
required: {
value: 'LOGIN.MESSAGES.USERNAME-REQUIRED'
},
minlength: {
value: 'LOGIN.MESSAGES.USERNAME-MIN',
params: {
minLength: this.minLength
}
}
},
password: {
required: {
value: 'LOGIN.MESSAGES.PASSWORD-REQUIRED'
}
}
};
}
private initFormFieldsDefault() {
this.form = this._fb.group({
username: ['', Validators.compose([Validators.required, Validators.minLength(this.minLength)])],
password: ['', Validators.required]
});
}
/**
* Disable the error flag
*/
private disableError() {
this.isError = false;
this.initFormError();
}
}