mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-2795] SSO implicitflow (#3332)
* Enable OAUTH2 * Create SSO services * SSO improvements * Rollback sso login change * Add SSO configuration from Setting component * Refactoring * Remove login ECM/BPM toggle and move use the userpreference instead of store * fix host setting unit test * Fix unit test missing instance * use the Js api oauth * add logout component and clean sso not used class * fix dependencies cicle * add translation settings * fix style setting page * clean * JS APi should receive the oauth config from the userPreference and not from the config file * change login if SSO is present * missing spaces * add sso test in login component * add logout directive new properties test * Improve host setting and remove library reference * fix login test * Remove unused code * Fix authentication unit test * fix authguard unit test * fix csrf check login component * fix unit test core and demo shell * remove
This commit is contained in:
committed by
Eugenio Romano
parent
3a6c12e624
commit
f8e92b2fb0
@@ -18,10 +18,10 @@
|
||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
import { AuthenticationService } from '../services/authentication.service';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { BpmProductVersionModel, EcmProductVersionModel } from '../models/product-version.model';
|
||||
import { DiscoveryApiService } from '../services/discovery-api.service';
|
||||
import { ObjectDataTableAdapter } from '../datatable/data/object-datatable-adapter';
|
||||
import { UserPreferencesService } from '../services/user-preferences.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-about',
|
||||
@@ -44,7 +44,7 @@ export class AboutComponent implements OnInit {
|
||||
bpmVersion: BpmProductVersionModel = null;
|
||||
|
||||
constructor(private http: Http,
|
||||
private appConfig: AppConfigService,
|
||||
private userPreference: UserPreferencesService,
|
||||
private authService: AuthenticationService,
|
||||
private discovery: DiscoveryApiService) {
|
||||
}
|
||||
@@ -114,8 +114,8 @@ export class AboutComponent implements OnInit {
|
||||
|
||||
});
|
||||
|
||||
this.ecmHost = this.appConfig.get<string>('ecmHost');
|
||||
this.bpmHost = this.appConfig.get<string>('bpmHost');
|
||||
this.ecmHost = this.userPreference.ecmHost;
|
||||
this.bpmHost = this.userPreference.bpmHost;
|
||||
}
|
||||
|
||||
private gitHubLinkCreation(alfrescoPackagesTableRepresentation): void {
|
||||
|
@@ -461,12 +461,17 @@
|
||||
"oauth2": {
|
||||
"description": "AUTH configuration parameters",
|
||||
"type": "object",
|
||||
"required": [ "host", "clientId", "secret" ],
|
||||
"required": [ "host", "clientId", "secret", "scope" ],
|
||||
"properties": {
|
||||
"host": { "type": "string" },
|
||||
"silentLogin": { "type": "boolean" },
|
||||
"authPath": { "type": "string" },
|
||||
"clientId": { "type": "string" },
|
||||
"secret": { "type": "string" }
|
||||
"secret": { "type": "string" },
|
||||
"redirectUri": { "type": "string" },
|
||||
"redirectUriLogout": { "type": "string" },
|
||||
"silentRefreshRedirectUri": { "type": "string" },
|
||||
"scope": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"adf-version-manager": {
|
||||
|
@@ -17,6 +17,7 @@
|
||||
|
||||
import { CommonModule, DatePipe } from '@angular/common';
|
||||
import { HttpClient, HttpClientModule } from '@angular/common/http';
|
||||
|
||||
import { APP_INITIALIZER, NgModule, ModuleWithProviders } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, ContentChildren } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
@@ -23,56 +23,152 @@ import { Observable } from 'rxjs/Observable';
|
||||
import { AuthenticationService } from '../services';
|
||||
import { setupTestBed } from '../testing/setupTestBed';
|
||||
import { CoreModule } from '../core.module';
|
||||
import { LogoutDirective } from './logout.directive';
|
||||
|
||||
describe('LogoutDirective', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'adf-test-component',
|
||||
template: '<button adf-logout></button>'
|
||||
})
|
||||
class TestComponent {}
|
||||
describe('No input', () => {
|
||||
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
let router: Router;
|
||||
let authService: AuthenticationService;
|
||||
@Component({
|
||||
selector: 'adf-test-component',
|
||||
template: '<button adf-logout></button>'
|
||||
})
|
||||
class TestComponent {
|
||||
@ContentChildren(LogoutDirective)
|
||||
logoutDirective: LogoutDirective;
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
let router: Router;
|
||||
let authService: AuthenticationService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot(),
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
TestComponent
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
router = TestBed.get(Router);
|
||||
authService = TestBed.get(AuthenticationService);
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should redirect to login on click', () => {
|
||||
spyOn(router, 'navigate').and.callThrough();
|
||||
spyOn(authService, 'logout').and.returnValue(Observable.of(true));
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button');
|
||||
button.click();
|
||||
|
||||
expect(authService.logout).toHaveBeenCalled();
|
||||
expect(router.navigate).toHaveBeenCalledWith(['/login']);
|
||||
});
|
||||
|
||||
it('should redirect to login even on logout error', () => {
|
||||
spyOn(router, 'navigate').and.callThrough();
|
||||
spyOn(authService, 'logout').and.returnValue(Observable.throw('err'));
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button');
|
||||
button.click();
|
||||
|
||||
expect(authService.logout).toHaveBeenCalled();
|
||||
expect(router.navigate).toHaveBeenCalledWith(['/login']);
|
||||
});
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot(),
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
TestComponent
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
router = TestBed.get(Router);
|
||||
authService = TestBed.get(AuthenticationService);
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.detectChanges();
|
||||
describe('redirectUri', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'adf-test-component',
|
||||
template: '<button adf-logout redirectUri="/myCustomUri"></button>'
|
||||
})
|
||||
class TestComponent {
|
||||
@ContentChildren(LogoutDirective)
|
||||
logoutDirective: LogoutDirective;
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
let router: Router;
|
||||
let authService: AuthenticationService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot(),
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
TestComponent
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
router = TestBed.get(Router);
|
||||
authService = TestBed.get(AuthenticationService);
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should redirect to the the input redirectUri on click if present', () => {
|
||||
spyOn(router, 'navigate').and.callThrough();
|
||||
spyOn(authService, 'logout').and.returnValue(Observable.of(true));
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button');
|
||||
button.click();
|
||||
|
||||
expect(authService.logout).toHaveBeenCalled();
|
||||
expect(router.navigate).toHaveBeenCalledWith(['/myCustomUri']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should redirect to login on click', () => {
|
||||
spyOn(router, 'navigate').and.callThrough();
|
||||
spyOn(authService, 'logout').and.returnValue(Observable.of(true));
|
||||
describe('redirectUri', () => {
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button');
|
||||
button.click();
|
||||
@Component({
|
||||
selector: 'adf-test-component',
|
||||
template: '<button adf-logout [enabelRedirect]="false"></button>'
|
||||
})
|
||||
class TestComponent {
|
||||
@ContentChildren(LogoutDirective)
|
||||
logoutDirective: LogoutDirective;
|
||||
}
|
||||
|
||||
expect(authService.logout).toHaveBeenCalled();
|
||||
expect(router.navigate).toHaveBeenCalledWith([ '/login' ]);
|
||||
});
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
let router: Router;
|
||||
let authService: AuthenticationService;
|
||||
|
||||
it('should redirect to login even on logout error', () => {
|
||||
spyOn(router, 'navigate').and.callThrough();
|
||||
spyOn(authService, 'logout').and.returnValue(Observable.throw('err'));
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot(),
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
TestComponent
|
||||
]
|
||||
});
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button');
|
||||
button.click();
|
||||
beforeEach(() => {
|
||||
router = TestBed.get(Router);
|
||||
authService = TestBed.get(AuthenticationService);
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
expect(authService.logout).toHaveBeenCalled();
|
||||
expect(router.navigate).toHaveBeenCalledWith([ '/login' ]);
|
||||
it('should not redirect if enabelRedirect is false', () => {
|
||||
spyOn(router, 'navigate').and.callThrough();
|
||||
spyOn(authService, 'logout').and.returnValue(Observable.of(true));
|
||||
const button = fixture.nativeElement.querySelector('button');
|
||||
button.click();
|
||||
|
||||
expect(authService.logout).toHaveBeenCalled();
|
||||
expect(router.navigate).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Directive, ElementRef, OnInit, Renderer2 } from '@angular/core';
|
||||
import { Input, Directive, ElementRef, OnInit, Renderer2 } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { AuthenticationService } from '../services/authentication.service';
|
||||
|
||||
@@ -24,11 +24,18 @@ import { AuthenticationService } from '../services/authentication.service';
|
||||
})
|
||||
export class LogoutDirective implements OnInit {
|
||||
|
||||
constructor(
|
||||
private elementRef: ElementRef,
|
||||
private renderer: Renderer2,
|
||||
private router: Router,
|
||||
private auth: AuthenticationService) {
|
||||
/** Uri to be redirect after the logout default value login */
|
||||
@Input()
|
||||
redirectUri: string = '/login';
|
||||
|
||||
/** Enable redirect after logout */
|
||||
@Input()
|
||||
enabelRedirect: boolean = true;
|
||||
|
||||
constructor(private elementRef: ElementRef,
|
||||
private renderer: Renderer2,
|
||||
private router: Router,
|
||||
private auth: AuthenticationService) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -42,12 +49,14 @@ export class LogoutDirective implements OnInit {
|
||||
|
||||
logout() {
|
||||
this.auth.logout().subscribe(
|
||||
() => this.redirectToLogin(),
|
||||
() => this.redirectToLogin()
|
||||
() => this.redirectToUri(),
|
||||
() => this.redirectToUri()
|
||||
);
|
||||
}
|
||||
|
||||
redirectToLogin() {
|
||||
this.router.navigate(['/login']);
|
||||
redirectToUri() {
|
||||
if (this.enabelRedirect) {
|
||||
this.router.navigate([this.redirectUri]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,20 +16,28 @@
|
||||
*/
|
||||
|
||||
import { SimpleChange } from '@angular/core';
|
||||
import { fakeAsync, tick } from '@angular/core/testing';
|
||||
import { fakeAsync, tick, TestBed } from '@angular/core/testing';
|
||||
import { NodeFavoriteDirective } from './node-favorite.directive';
|
||||
import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { StorageService } from '../services/storage.service';
|
||||
import { AlfrescoApiService } from '../services/alfresco-api.service';
|
||||
import { UserPreferencesService } from '../services/user-preferences.service';
|
||||
import { setupTestBed } from '../testing/setupTestBed';
|
||||
import { CoreTestingModule } from '../testing/core.testing.module';
|
||||
|
||||
describe('NodeFavoriteDirective', () => {
|
||||
|
||||
let directive;
|
||||
let alfrescoApiService: AlfrescoApiService;
|
||||
let alfrescoApiService;
|
||||
let userPreferences;
|
||||
|
||||
setupTestBed({
|
||||
imports: [CoreTestingModule]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), userPreferences, new StorageService());
|
||||
directive = new NodeFavoriteDirective( alfrescoApiService);
|
||||
});
|
||||
|
||||
|
@@ -118,6 +118,7 @@
|
||||
"ERROR_PLURAL": "{{ number }} items couldn't be deleted"
|
||||
},
|
||||
"HOST_SETTINGS": {
|
||||
"REQUIRED": "The field is required",
|
||||
"CS_URL_ERROR": "Content Services address doesn't match the URL format",
|
||||
"PS_URL_ERROR": "Process Services address doesn't match the URL format",
|
||||
"TITLE": "Settings",
|
||||
@@ -125,7 +126,11 @@
|
||||
"BP-HOST": "Process Services URL",
|
||||
"BACK": "Back",
|
||||
"APPLY": "APPLY",
|
||||
"NOT_VALID": "http(s)://host|ip:port(/path) not recognized, try a different URL."
|
||||
"NOT_VALID": "http(s)://host|ip:port(/path) not recognized, try a different URL.",
|
||||
"REDIRECT": "Redirect Uri",
|
||||
"SILENT": "Silent Login",
|
||||
"SCOPE": "Scope",
|
||||
"CLIENT": "ClientId"
|
||||
},
|
||||
"CARDVIEW": {
|
||||
"VALIDATORS": {
|
||||
@@ -193,7 +198,8 @@
|
||||
"BUTTON": {
|
||||
"LOGIN": "SIGN IN",
|
||||
"CHECKING": "CHECKING",
|
||||
"WELCOME": "WELCOME"
|
||||
"WELCOME": "WELCOME",
|
||||
"SSO": "SIGN IN SSO"
|
||||
},
|
||||
"ACTION": {
|
||||
"HELP": "NEED HELP?",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<div class="adf-login-content" [style.background-image]="'url(' + backgroundImageUrl + ')'">
|
||||
<div class="ie11FixerParent">
|
||||
<div class="ie11FixerChild">
|
||||
<div class="ie11FixerParent">
|
||||
<div class="ie11FixerChild">
|
||||
|
||||
<mat-card class="adf-login-card-wide">
|
||||
<form id="adf-login-form" [formGroup]="form" (submit)="onSubmit(form.value)" autocomplete="off">
|
||||
@@ -10,107 +10,128 @@
|
||||
<div class="adf-alfresco-logo">
|
||||
<!--HEADER TEMPLATE-->
|
||||
<ng-template *ngIf="headerTemplate"
|
||||
ngFor [ngForOf]="[data]"
|
||||
[ngForTemplate]="headerTemplate">
|
||||
ngFor [ngForOf]="[data]"
|
||||
[ngForTemplate]="headerTemplate">
|
||||
</ng-template>
|
||||
<img *ngIf="!headerTemplate" class="adf-img-logo" [src]="logoImageUrl"
|
||||
alt="{{'LOGIN.LOGO' | translate }}">
|
||||
alt="{{'LOGIN.LOGO' | translate }}">
|
||||
</div>
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
|
||||
<mat-card-content class="adf-login-controls">
|
||||
|
||||
<!--ERRORS AREA-->
|
||||
<div class="adf-error-container">
|
||||
<div *ngIf="isError" id="login-error" data-automation-id="login-error"
|
||||
class="error adf-error-message">
|
||||
<mat-icon class="error-icon">warning</mat-icon>
|
||||
<span class="login-error-message">{{errorMsg | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--USERNAME FIELD-->
|
||||
<div class="adf-login__field" [ngClass]="{'is-invalid': isErrorStyle(form.controls.username)}">
|
||||
<mat-form-field class="adf-full-width" floatPlaceholder="never" color="primary">
|
||||
<input matInput placeholder="{{'LOGIN.LABEL.USERNAME' | translate }}"
|
||||
type="text"
|
||||
class="adf-full-width"
|
||||
[formControl]="form.controls['username']"
|
||||
autocapitalize="none"
|
||||
id="username"
|
||||
data-automation-id="username"
|
||||
(blur)="trimUsername($event)"
|
||||
tabindex="1">
|
||||
</mat-form-field>
|
||||
|
||||
<span class="adf-login-validation" for="username" *ngIf="formError.username">
|
||||
<span id="username-error" class="adf-login-error" data-automation-id="username-error">{{formError.username | translate }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!--PASSWORD FIELD-->
|
||||
<div class="adf-login__field">
|
||||
<mat-form-field class="adf-full-width" floatPlaceholder="never" color="primary">
|
||||
<input matInput placeholder="{{'LOGIN.LABEL.PASSWORD' | translate }}"
|
||||
type="password"
|
||||
[formControl]="form.controls['password']"
|
||||
id="password"
|
||||
data-automation-id="password"
|
||||
tabindex="2">
|
||||
<mat-icon *ngIf="isPasswordShow" matSuffix class="adf-login-password-icon"
|
||||
data-automation-id="hide_password" (click)="toggleShowPassword()">visibility
|
||||
</mat-icon>
|
||||
<mat-icon *ngIf="!isPasswordShow" matSuffix class="adf-login-password-icon"
|
||||
data-automation-id="show_password" (click)="toggleShowPassword()">visibility_off
|
||||
</mat-icon>
|
||||
</mat-form-field>
|
||||
<span class="adf-login-validation" for="password" *ngIf="formError.password">
|
||||
<span id="password-required" class="adf-login-error"
|
||||
data-automation-id="password-required">{{formError.password | translate }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!--CUSTOM CONTENT-->
|
||||
<ng-content></ng-content>
|
||||
|
||||
<br>
|
||||
<button type="submit" id="login-button" tabindex="3"
|
||||
class="adf-login-button"
|
||||
mat-raised-button color="primary"
|
||||
[class.isChecking]="actualLoginStep === LoginSteps.Checking"
|
||||
[class.isWelcome]="actualLoginStep === LoginSteps.Welcome"
|
||||
data-automation-id="login-button" [disabled]="!form.valid">
|
||||
|
||||
<span *ngIf="actualLoginStep === LoginSteps.Landing" class="adf-login-button-label">{{ 'LOGIN.BUTTON.LOGIN' | translate }}</span>
|
||||
|
||||
<div *ngIf="actualLoginStep === LoginSteps.Checking" class="adf-interactive-login-label">
|
||||
<span class="adf-login-button-label">{{ 'LOGIN.BUTTON.CHECKING' | translate }}</span>
|
||||
<div class="adf-login-spinner-container">
|
||||
<mat-spinner id="checking-spinner" class="adf-login-checking-spinner" [diameter]="25"></mat-spinner>
|
||||
<div *ngIf="!implicitFlow">
|
||||
<!--ERRORS AREA-->
|
||||
<div class="adf-error-container">
|
||||
<div *ngIf="isError" id="login-error" data-automation-id="login-error"
|
||||
class="error adf-error-message">
|
||||
<mat-icon class="error-icon">warning</mat-icon>
|
||||
<span class="login-error-message">{{errorMsg | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--USERNAME FIELD-->
|
||||
<div class="adf-login__field"
|
||||
[ngClass]="{'is-invalid': isErrorStyle(form.controls.username)}">
|
||||
<mat-form-field class="adf-full-width" floatPlaceholder="never" color="primary">
|
||||
<input matInput placeholder="{{'LOGIN.LABEL.USERNAME' | translate }}"
|
||||
type="text"
|
||||
class="adf-full-width"
|
||||
[formControl]="form.controls['username']"
|
||||
autocapitalize="none"
|
||||
id="username"
|
||||
data-automation-id="username"
|
||||
(blur)="trimUsername($event)"
|
||||
tabindex="1">
|
||||
</mat-form-field>
|
||||
|
||||
<div *ngIf="actualLoginStep === LoginSteps.Welcome" class="adf-interactive-login-label">
|
||||
<span class="adf-login-button-label">{{ 'LOGIN.BUTTON.WELCOME' | translate }}</span>
|
||||
<mat-icon class="welcome-icon">done</mat-icon>
|
||||
<span class="adf-login-validation" for="username" *ngIf="formError.username">
|
||||
<span id="username-error" class="adf-login-error" data-automation-id="username-error">{{formError.username | translate }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</button>
|
||||
<div *ngIf="showRememberMe" class="adf-login__remember-me">
|
||||
<mat-checkbox id="adf-login-remember" color="primary" class="adf-login-rememberme" [checked]="rememberMe"
|
||||
(change)="rememberMe = !rememberMe">{{ 'LOGIN.LABEL.REMEMBER' | translate }}
|
||||
</mat-checkbox>
|
||||
<!--PASSWORD FIELD-->
|
||||
<div class="adf-login__field">
|
||||
<mat-form-field class="adf-full-width" floatPlaceholder="never" color="primary">
|
||||
<input matInput placeholder="{{'LOGIN.LABEL.PASSWORD' | translate }}"
|
||||
type="password"
|
||||
[formControl]="form.controls['password']"
|
||||
id="password"
|
||||
data-automation-id="password"
|
||||
tabindex="2">
|
||||
<mat-icon *ngIf="isPasswordShow" matSuffix class="adf-login-password-icon"
|
||||
data-automation-id="hide_password" (click)="toggleShowPassword()">
|
||||
visibility
|
||||
</mat-icon>
|
||||
<mat-icon *ngIf="!isPasswordShow" matSuffix class="adf-login-password-icon"
|
||||
data-automation-id="show_password" (click)="toggleShowPassword()">
|
||||
visibility_off
|
||||
</mat-icon>
|
||||
</mat-form-field>
|
||||
<span class="adf-login-validation" for="password" *ngIf="formError.password">
|
||||
<span id="password-required" class="adf-login-error"
|
||||
data-automation-id="password-required">{{formError.password | translate }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!--CUSTOM CONTENT-->
|
||||
<ng-content></ng-content>
|
||||
|
||||
<br>
|
||||
<button type="submit" id="login-button" tabindex="3"
|
||||
class="adf-login-button"
|
||||
mat-raised-button color="primary"
|
||||
[class.isChecking]="actualLoginStep === LoginSteps.Checking"
|
||||
[class.isWelcome]="actualLoginStep === LoginSteps.Welcome"
|
||||
data-automation-id="login-button" [disabled]="!form.valid">
|
||||
|
||||
<span *ngIf="actualLoginStep === LoginSteps.Landing" class="adf-login-button-label">{{ 'LOGIN.BUTTON.LOGIN' | translate }}</span>
|
||||
|
||||
<div *ngIf="actualLoginStep === LoginSteps.Checking"
|
||||
class="adf-interactive-login-label">
|
||||
<span
|
||||
class="adf-login-button-label">{{ 'LOGIN.BUTTON.CHECKING' | translate }}</span>
|
||||
<div class="adf-login-spinner-container">
|
||||
<mat-spinner id="checking-spinner" class="adf-login-checking-spinner"
|
||||
[diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div *ngIf="actualLoginStep === LoginSteps.Welcome" class="adf-interactive-login-label">
|
||||
<span class="adf-login-button-label">{{ 'LOGIN.BUTTON.WELCOME' | translate }}</span>
|
||||
<mat-icon class="welcome-icon">done</mat-icon>
|
||||
</div>
|
||||
|
||||
</button>
|
||||
<div *ngIf="showRememberMe" class="adf-login__remember-me">
|
||||
<mat-checkbox id="adf-login-remember" color="primary" class="adf-login-rememberme"
|
||||
[checked]="rememberMe"
|
||||
(change)="rememberMe = !rememberMe">{{ 'LOGIN.LABEL.REMEMBER' | translate
|
||||
}}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="implicitFlow">
|
||||
<button type="button" (click)="implicitLogin()" id="login-button-sso" tabindex="1"
|
||||
class="adf-login-button"
|
||||
mat-raised-button color="primary"
|
||||
data-automation-id="login-button-sso">
|
||||
<span class="adf-login-button-label">{{ 'LOGIN.BUTTON.SSO' | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions *ngIf="footerTemplate || showLoginActions">
|
||||
|
||||
<div class="adf-login-action-container">
|
||||
<!--FOOTER TEMPLATE-->
|
||||
<ng-template *ngIf="footerTemplate"
|
||||
ngFor [ngForOf]="[data]"
|
||||
[ngForTemplate]="footerTemplate">
|
||||
ngFor [ngForOf]="[data]"
|
||||
[ngForTemplate]="footerTemplate">
|
||||
</ng-template>
|
||||
<div class="adf-login-action" *ngIf="!footerTemplate && showLoginActions">
|
||||
<div id="adf-login-action-left" class="adf-login-action-left">
|
||||
@@ -131,5 +152,5 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -58,7 +58,7 @@ describe('LoginComponent', () => {
|
||||
imports: [CoreTestingModule]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
|
||||
element = fixture.nativeElement;
|
||||
@@ -66,15 +66,17 @@ describe('LoginComponent', () => {
|
||||
component.showRememberMe = true;
|
||||
component.showLoginActions = true;
|
||||
|
||||
usernameInput = element.querySelector('#username');
|
||||
passwordInput = element.querySelector('#password');
|
||||
|
||||
authService = TestBed.get(AuthenticationService);
|
||||
router = TestBed.get(Router);
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
usernameInput = element.querySelector('#username');
|
||||
passwordInput = element.querySelector('#password');
|
||||
});
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
@@ -98,7 +100,7 @@ describe('LoginComponent', () => {
|
||||
});
|
||||
|
||||
it('should redirect to route on successful login', () => {
|
||||
spyOn(authService, 'login').and.returnValue(Observable.of({ type: 'type', ticket: 'ticket'}));
|
||||
spyOn(authService, 'login').and.returnValue(Observable.of({ type: 'type', ticket: 'ticket' }));
|
||||
const redirect = '/home';
|
||||
component.successRoute = redirect;
|
||||
spyOn(router, 'navigate');
|
||||
@@ -107,10 +109,10 @@ describe('LoginComponent', () => {
|
||||
});
|
||||
|
||||
it('should redirect to previous route state on successful login', () => {
|
||||
spyOn(authService, 'login').and.returnValue(Observable.of({ type: 'type', ticket: 'ticket'}));
|
||||
spyOn(authService, 'login').and.returnValue(Observable.of({ type: 'type', ticket: 'ticket' }));
|
||||
const redirect = '/home';
|
||||
component.successRoute = redirect;
|
||||
authService.setRedirect({ provider: 'ECM', navigation: ['some-route'] } );
|
||||
authService.setRedirect({ provider: 'ECM', navigation: ['some-route'] });
|
||||
|
||||
spyOn(router, 'navigate');
|
||||
|
||||
@@ -158,7 +160,7 @@ describe('LoginComponent', () => {
|
||||
});
|
||||
|
||||
it('should be changed to the "welcome key" after a successful login attempt', () => {
|
||||
spyOn(authService, 'login').and.returnValue(Observable.of({ type: 'type', ticket: 'ticket'}));
|
||||
spyOn(authService, 'login').and.returnValue(Observable.of({ type: 'type', ticket: 'ticket' }));
|
||||
loginWithCredentials('fake-username', 'fake-password');
|
||||
|
||||
expect(getLoginButtonText()).toEqual('LOGIN.BUTTON.WELCOME');
|
||||
@@ -382,7 +384,7 @@ describe('LoginComponent', () => {
|
||||
});
|
||||
|
||||
it('should return success event after the login have succeeded', (done) => {
|
||||
spyOn(authService, 'login').and.returnValue(Observable.of({ type: 'type', ticket: 'ticket'}));
|
||||
spyOn(authService, 'login').and.returnValue(Observable.of({ type: 'type', ticket: 'ticket' }));
|
||||
|
||||
component.providers = 'ECM';
|
||||
expect(component.isError).toBe(false);
|
||||
@@ -514,7 +516,7 @@ describe('LoginComponent', () => {
|
||||
|
||||
expect(component.isError).toBe(false);
|
||||
expect(event).toEqual(
|
||||
new LoginSuccessEvent({type: 'type', ticket: 'ticket'}, 'fake-username', null)
|
||||
new LoginSuccessEvent({ type: 'type', ticket: 'ticket' }, 'fake-username', null)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -583,4 +585,47 @@ describe('LoginComponent', () => {
|
||||
|
||||
loginWithCredentials('fake-username', 'fake-password');
|
||||
}));
|
||||
|
||||
describe('SSO', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
userPreferences.oauthConfig = { implicitFlow: true };
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
userPreferences.oauthConfig = null;
|
||||
});
|
||||
|
||||
it('should not show login username and password if SSO implicit flow is active', async(() => {
|
||||
spyOn(authService, 'isOauth').and.returnValue(true);
|
||||
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('#username')).toBeNull();
|
||||
expect(element.querySelector('#password')).toBeNull();
|
||||
|
||||
}));
|
||||
|
||||
it('should not show the login base auth button', async(() => {
|
||||
spyOn(authService, 'isOauth').and.returnValue(true);
|
||||
userPreferences.oauthConfig = { implicitFlow: true };
|
||||
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('#login-button')).toBeNull();
|
||||
}));
|
||||
|
||||
it('should show the login SSO button', async(() => {
|
||||
spyOn(authService, 'isOauth').and.returnValue(true);
|
||||
userPreferences.oauthConfig = { implicitFlow: true };
|
||||
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('#login-button-sso')).toBeDefined();
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
@@ -120,6 +120,8 @@ export class LoginComponent implements OnInit {
|
||||
@Output()
|
||||
executeSubmit = new EventEmitter<LoginSubmitEvent>();
|
||||
|
||||
implicitFlow: boolean = false;
|
||||
|
||||
form: FormGroup;
|
||||
isError: boolean = false;
|
||||
errorMsg: string;
|
||||
@@ -154,6 +156,12 @@ export class LoginComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.authService.isOauth()) {
|
||||
if (this.userPreferences.oauthConfig && this.userPreferences.oauthConfig.implicitFlow) {
|
||||
this.implicitFlow = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasCustomFiledsValidation()) {
|
||||
this.form = this._fb.group(this.fieldsValidation);
|
||||
} else {
|
||||
@@ -178,7 +186,7 @@ export class LoginComponent implements OnInit {
|
||||
this.settingsService.csrfDisabled = this.disableCsrf;
|
||||
|
||||
this.disableError();
|
||||
const args = new LoginSubmitEvent({controls : { username : this.form.controls.username} });
|
||||
const args = new LoginSubmitEvent({ controls: { username: this.form.controls.username } });
|
||||
this.executeSubmit.emit(args);
|
||||
|
||||
if (args.defaultPrevented) {
|
||||
@@ -188,6 +196,10 @@ export class LoginComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
implicitLogin() {
|
||||
this.authService.ssoImplictiLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* The method check the error in the form and push the error in the formError object
|
||||
* @param data
|
||||
|
@@ -16,49 +16,21 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlfrescoApi } from 'alfresco-js-api';
|
||||
import * as alfrescoApi from 'alfresco-js-api';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { StorageService } from '../services/storage.service';
|
||||
import { AlfrescoApiService } from '../services/alfresco-api.service';
|
||||
import { UserPreferencesService } from '../services/user-preferences.service';
|
||||
|
||||
/* tslint:disable:adf-file-name */
|
||||
@Injectable()
|
||||
export class AlfrescoApiServiceMock extends AlfrescoApiService {
|
||||
|
||||
constructor(protected appConfig: AppConfigService,
|
||||
protected userPreference: UserPreferencesService,
|
||||
protected storage: StorageService) {
|
||||
super(appConfig, storage);
|
||||
super(appConfig, userPreference, storage);
|
||||
if (!this.alfrescoApi) {
|
||||
this.initAlfrescoApi();
|
||||
}
|
||||
}
|
||||
|
||||
async load() {
|
||||
await this.appConfig.load().then(() => {
|
||||
if (!this.alfrescoApi) {
|
||||
this.initAlfrescoApi();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async reset() {
|
||||
if (this.alfrescoApi) {
|
||||
this.alfrescoApi = null;
|
||||
}
|
||||
this.initAlfrescoApi();
|
||||
}
|
||||
|
||||
protected initAlfrescoApi() {
|
||||
this.alfrescoApi = <AlfrescoApi> new alfrescoApi({
|
||||
provider: this.storage.getItem('AUTH_TYPE'),
|
||||
ticketEcm: this.storage.getItem('ticket-ECM'),
|
||||
ticketBpm: this.storage.getItem('ticket-BPM'),
|
||||
hostEcm: this.appConfig.get<string>('ecmHost'),
|
||||
hostBpm: this.appConfig.get<string>('bpmHost'),
|
||||
contextRoot: 'alfresco',
|
||||
disableCsrf: this.storage.getItem('DISABLE_CSRF') === 'true',
|
||||
oauth2: this.appConfig.get<any>('oauth2')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
29
lib/core/models/oauth-config.model.ts
Normal file
29
lib/core/models/oauth-config.model.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
export interface OauthConfigModel {
|
||||
host: string;
|
||||
clientId: string;
|
||||
scope: string;
|
||||
implicitFlow: boolean;
|
||||
redirectUri: string;
|
||||
silentLogin?: boolean;
|
||||
silentRefreshRedirectUri?: string;
|
||||
secret?: string;
|
||||
refreshTokenTimeout?: number;
|
||||
redirectUriLogout?: string;
|
||||
}
|
@@ -25,6 +25,7 @@ import * as alfrescoApi from 'alfresco-js-api';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { StorageService } from './storage.service';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { UserPreferencesService } from './user-preferences.service';
|
||||
|
||||
/* tslint:disable:adf-file-name */
|
||||
|
||||
@@ -95,6 +96,7 @@ export class AlfrescoApiService {
|
||||
}
|
||||
|
||||
constructor(protected appConfig: AppConfigService,
|
||||
protected userPreference: UserPreferencesService,
|
||||
protected storage: StorageService) {
|
||||
}
|
||||
|
||||
@@ -105,23 +107,32 @@ export class AlfrescoApiService {
|
||||
}
|
||||
|
||||
async reset() {
|
||||
if (this.alfrescoApi) {
|
||||
this.alfrescoApi = null;
|
||||
}
|
||||
this.initAlfrescoApi();
|
||||
}
|
||||
|
||||
protected initAlfrescoApi() {
|
||||
this.alfrescoApi = <AlfrescoApi> new alfrescoApi({
|
||||
provider: this.storage.getItem('AUTH_TYPE'),
|
||||
let oauth: any = Object.assign({}, this.userPreference.oauthConfig);
|
||||
if (oauth) {
|
||||
oauth.redirectUri = window.location.origin + (oauth.redirectUri || '/');
|
||||
oauth.redirectUriLogout = window.location.origin + (oauth.redirectUriLogout || '/');
|
||||
}
|
||||
|
||||
const config = {
|
||||
provider: this.userPreference.providers,
|
||||
ticketEcm: this.storage.getItem('ticket-ECM'),
|
||||
ticketBpm: this.storage.getItem('ticket-BPM'),
|
||||
hostEcm: this.appConfig.get<string>('ecmHost'),
|
||||
hostBpm: this.appConfig.get<string>('bpmHost'),
|
||||
hostEcm: this.userPreference.ecmHost,
|
||||
hostBpm: this.userPreference.bpmHost,
|
||||
contextRootBpm: this.appConfig.get<string>('contextRootBpm'),
|
||||
contextRoot: this.appConfig.get<string>('contextRootEcm'),
|
||||
disableCsrf: this.storage.getItem('DISABLE_CSRF') === 'true',
|
||||
oauth2: this.appConfig.get<any>('oauth2')
|
||||
});
|
||||
oauth2: oauth
|
||||
};
|
||||
|
||||
if (this.alfrescoApi) {
|
||||
this.alfrescoApi.configureJsApi(config);
|
||||
} else {
|
||||
this.alfrescoApi = <AlfrescoApi> new alfrescoApi(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -35,6 +35,7 @@ describe('AuthGuardService BPM', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
authService = TestBed.get(AuthenticationService);
|
||||
authGuard = TestBed.get(AuthGuardBpm);
|
||||
routerService = TestBed.get(Router);
|
||||
|
@@ -38,7 +38,7 @@ export class AuthGuardEcm implements CanActivate {
|
||||
}
|
||||
|
||||
private isLoggedIn(): Promise<boolean> {
|
||||
if (!this.authApi.isLoggedIn()) {
|
||||
if (this.authApi === undefined || !this.authApi.isLoggedIn()) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
|
@@ -35,6 +35,7 @@ describe('AuthGuardService', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
state = { url: '' };
|
||||
authService = TestBed.get(AuthenticationService);
|
||||
router = TestBed.get(Router);
|
||||
|
@@ -21,22 +21,24 @@ import {
|
||||
CanActivateChild, RouterStateSnapshot, Router,
|
||||
PRIMARY_OUTLET, UrlTree, UrlSegmentGroup, UrlSegment
|
||||
} from '@angular/router';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { AuthenticationService } from './authentication.service';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { UserPreferencesService } from './user-preferences.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthGuard implements CanActivate, CanActivateChild {
|
||||
constructor(private authService: AuthenticationService,
|
||||
private router: Router,
|
||||
private userPreference: UserPreferencesService,
|
||||
private appConfig: AppConfigService) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean> {
|
||||
const redirectUrl = state.url;
|
||||
|
||||
return this.checkLogin(redirectUrl);
|
||||
}
|
||||
|
||||
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean> {
|
||||
return this.canActivate(route, state);
|
||||
}
|
||||
|
||||
@@ -44,23 +46,29 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
||||
if (this.authService.isLoggedIn()) {
|
||||
return true;
|
||||
}
|
||||
if (!this.authService.isOauth() || this.isOAuthWithoutSilentLogin() ) {
|
||||
const navigation = this.getNavigationCommands(redirectUrl);
|
||||
|
||||
const navigation = this.getNavigationCommands(redirectUrl);
|
||||
this.authService.setRedirect({ provider: 'ALL', navigation } );
|
||||
|
||||
this.authService.setRedirect({ provider: 'ALL', navigation } );
|
||||
const pathToLogin = this.getRouteDestinationForLogin();
|
||||
this.router.navigate(['/' + pathToLogin]);
|
||||
const pathToLogin = this.getRouteDestinationForLogin();
|
||||
this.router.navigate(['/' + pathToLogin]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private getRouteDestinationForLogin(): string {
|
||||
isOAuthWithoutSilentLogin() {
|
||||
return this.authService.isOauth() && this.userPreference.oauthConfig.silentLogin === false;
|
||||
}
|
||||
|
||||
public getRouteDestinationForLogin(): string {
|
||||
return this.appConfig &&
|
||||
this.appConfig.get<string>('loginRoute') ?
|
||||
this.appConfig.get<string>('loginRoute') : 'login';
|
||||
}
|
||||
|
||||
private getNavigationCommands(redirectUrl: string): any[] {
|
||||
public getNavigationCommands(redirectUrl: string): any[] {
|
||||
const urlTree: UrlTree = this.router.parseUrl(redirectUrl);
|
||||
const urlSegmentGroup: UrlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET];
|
||||
|
||||
|
@@ -60,7 +60,8 @@ describe('AuthenticationService', () => {
|
||||
describe('remember me', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
preferences.authType = 'ECM';
|
||||
preferences.providers = 'ECM';
|
||||
apiService.reset();
|
||||
});
|
||||
|
||||
it('[ECM] should save the remember me cookie as a session cookie after successful login', (done) => {
|
||||
@@ -123,7 +124,8 @@ describe('AuthenticationService', () => {
|
||||
describe('when the setting is ECM', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
preferences.authType = 'ECM';
|
||||
preferences.providers = 'ECM';
|
||||
apiService.reset();
|
||||
});
|
||||
|
||||
it('should require remember me set for ECM check', () => {
|
||||
@@ -154,7 +156,7 @@ describe('AuthenticationService', () => {
|
||||
});
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
'status': 201,
|
||||
status: 201,
|
||||
contentType: 'application/json',
|
||||
responseText: JSON.stringify({ 'entry': { 'id': 'fake-post-ticket', 'userId': 'admin' } })
|
||||
});
|
||||
@@ -274,26 +276,27 @@ describe('AuthenticationService', () => {
|
||||
it('[ECM] should set/get redirectUrl when provider is ECM', () => {
|
||||
authService.setRedirect({ provider: 'ECM', navigation: ['some-url'] });
|
||||
|
||||
expect(authService.getRedirect(preferences.authType)).toEqual(['some-url']);
|
||||
expect(authService.getRedirect(preferences.providers)).toEqual(['some-url']);
|
||||
});
|
||||
|
||||
it('[ECM] should set/get redirectUrl when provider is BPM', () => {
|
||||
authService.setRedirect({ provider: 'BPM', navigation: ['some-url'] });
|
||||
|
||||
expect(authService.getRedirect(preferences.authType)).toBeNull();
|
||||
expect(authService.getRedirect(preferences.providers)).toBeNull();
|
||||
});
|
||||
|
||||
it('[ECM] should return null as redirectUrl when redirectUrl field is not set', () => {
|
||||
authService.setRedirect(null);
|
||||
|
||||
expect(authService.getRedirect(preferences.authType)).toBeNull();
|
||||
expect(authService.getRedirect(preferences.providers)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the setting is BPM', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
preferences.authType = 'BPM';
|
||||
preferences.providers = 'BPM';
|
||||
apiService.reset();
|
||||
});
|
||||
|
||||
it('should require remember me set for BPM check', () => {
|
||||
@@ -426,26 +429,27 @@ describe('AuthenticationService', () => {
|
||||
it('[BPM] should set/get redirectUrl when provider is BPM', () => {
|
||||
authService.setRedirect({ provider: 'BPM', navigation: ['some-url'] });
|
||||
|
||||
expect(authService.getRedirect(preferences.authType)).toEqual(['some-url']);
|
||||
expect(authService.getRedirect(preferences.providers)).toEqual(['some-url']);
|
||||
});
|
||||
|
||||
it('[BPM] should set/get redirectUrl when provider is ECM', () => {
|
||||
authService.setRedirect({ provider: 'ECM', navigation: ['some-url'] });
|
||||
|
||||
expect(authService.getRedirect(preferences.authType)).toBeNull();
|
||||
expect(authService.getRedirect(preferences.providers)).toBeNull();
|
||||
});
|
||||
|
||||
it('[BPM] should return null as redirectUrl when redirectUrl field is not set', () => {
|
||||
authService.setRedirect(null);
|
||||
|
||||
expect(authService.getRedirect(preferences.authType)).toBeNull();
|
||||
expect(authService.getRedirect(preferences.providers)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the setting is both ECM and BPM ', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
preferences.authType = 'ALL';
|
||||
preferences.providers = 'ALL';
|
||||
apiService.reset();
|
||||
});
|
||||
|
||||
it('[ALL] should return both ECM and BPM tickets after the login done', (done) => {
|
||||
@@ -542,25 +546,25 @@ describe('AuthenticationService', () => {
|
||||
it('[ALL] should set/get redirectUrl when provider is ALL', () => {
|
||||
authService.setRedirect({ provider: 'ALL', navigation: ['some-url'] });
|
||||
|
||||
expect(authService.getRedirect(preferences.authType)).toEqual(['some-url']);
|
||||
expect(authService.getRedirect(preferences.providers)).toEqual(['some-url']);
|
||||
});
|
||||
|
||||
it('[ALL] should set/get redirectUrl when provider is BPM', () => {
|
||||
authService.setRedirect({ provider: 'BPM', navigation: ['some-url'] });
|
||||
|
||||
expect(authService.getRedirect(preferences.authType)).toEqual(['some-url']);
|
||||
expect(authService.getRedirect(preferences.providers)).toEqual(['some-url']);
|
||||
});
|
||||
|
||||
it('[ALL] should set/get redirectUrl when provider is ECM', () => {
|
||||
authService.setRedirect({ provider: 'ECM', navigation: ['some-url'] });
|
||||
|
||||
expect(authService.getRedirect(preferences.authType)).toEqual(['some-url']);
|
||||
expect(authService.getRedirect(preferences.providers)).toEqual(['some-url']);
|
||||
});
|
||||
|
||||
it('[ALL] should return null as redirectUrl when redirectUrl field is not set', () => {
|
||||
authService.setRedirect(null);
|
||||
|
||||
expect(authService.getRedirect(preferences.authType)).toBeNull();
|
||||
expect(authService.getRedirect(preferences.providers)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -54,6 +54,10 @@ export class AuthenticationService {
|
||||
return !!this.alfrescoApi.getInstance().isLoggedIn();
|
||||
}
|
||||
|
||||
isOauth(): boolean {
|
||||
return this.alfrescoApi.getInstance().isOauthConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the user in.
|
||||
* @param username Username for the login
|
||||
@@ -63,19 +67,26 @@ export class AuthenticationService {
|
||||
*/
|
||||
login(username: string, password: string, rememberMe: boolean = false): Observable<{ type: string, ticket: any }> {
|
||||
this.removeTicket();
|
||||
return Observable.fromPromise(this.callApiLogin(username, password))
|
||||
return Observable.fromPromise(this.alfrescoApi.getInstance().login(username, password))
|
||||
.map((response: any) => {
|
||||
this.saveRememberMeCookie(rememberMe);
|
||||
this.saveTickets();
|
||||
this.onLogin.next(response);
|
||||
return {
|
||||
type: this.preferences.authType,
|
||||
type: this.preferences.providers,
|
||||
ticket: response
|
||||
};
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the user in with SSO
|
||||
*/
|
||||
ssoImplictiLogin() {
|
||||
this.alfrescoApi.getInstance().implicitLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the "remember me" cookie as either a long-life cookie or a session cookie.
|
||||
* @param rememberMe Enables a long-life cookie
|
||||
@@ -100,15 +111,6 @@ export class AuthenticationService {
|
||||
return (this.cookie.getItem(REMEMBER_ME_COOKIE_KEY) === null) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the alfresco Api with user and password end call the login method
|
||||
* @param username
|
||||
* @param password
|
||||
*/
|
||||
private callApiLogin(username: string, password: string) {
|
||||
return this.alfrescoApi.getInstance().login(username, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the user out.
|
||||
* @returns Response event called when logout is complete
|
||||
@@ -213,7 +215,7 @@ export class AuthenticationService {
|
||||
if (this.cookie.isEnabled() && !this.isRememberMeSet()) {
|
||||
return false;
|
||||
}
|
||||
return this.alfrescoApi.getInstance().ecmAuth && !!this.alfrescoApi.getInstance().ecmAuth.isLoggedIn();
|
||||
return this.alfrescoApi.getInstance().isEcmLoggedIn();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +226,7 @@ export class AuthenticationService {
|
||||
if (this.cookie.isEnabled() && !this.isRememberMeSet()) {
|
||||
return false;
|
||||
}
|
||||
return this.alfrescoApi.getInstance().bpmAuth && !!this.alfrescoApi.getInstance().bpmAuth.isLoggedIn();
|
||||
return this.alfrescoApi.getInstance().isBpmLoggedIn();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,7 +234,7 @@ export class AuthenticationService {
|
||||
* @returns The ECM username
|
||||
*/
|
||||
getEcmUsername(): string {
|
||||
return this.alfrescoApi.getInstance().ecmAuth.username;
|
||||
return this.alfrescoApi.getInstance().getEcmUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +242,7 @@ export class AuthenticationService {
|
||||
* @returns The BPM username
|
||||
*/
|
||||
getBpmUsername(): string {
|
||||
return this.alfrescoApi.getInstance().bpmAuth.username;
|
||||
return this.alfrescoApi.getInstance().getBpmUsername();
|
||||
}
|
||||
|
||||
/** Sets the URL to redirect to after login.
|
||||
|
@@ -17,6 +17,7 @@
|
||||
|
||||
import { inject, TestBed } from '@angular/core/testing';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { PageTitleService } from './page-title.service';
|
||||
@@ -54,7 +55,8 @@ class TestConfig {
|
||||
get: () => this.setup.applicationName,
|
||||
load: () => {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
onLoad: Observable.of({})
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -23,25 +23,18 @@ import { UploadService } from './upload.service';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { AlfrescoApiService } from './alfresco-api.service';
|
||||
import { StorageService } from './storage.service';
|
||||
import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock';
|
||||
|
||||
import { setupTestBed } from '../testing/setupTestBed';
|
||||
import { CoreTestingModule } from '../testing/core.testing.module';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
describe('UploadService', () => {
|
||||
let service: UploadService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
AppConfigModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
|
||||
StorageService,
|
||||
UploadService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [CoreTestingModule, AppConfigModule]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
let appConfig: AppConfigService = TestBed.get(AppConfigService);
|
||||
@@ -53,6 +46,8 @@ describe('UploadService', () => {
|
||||
};
|
||||
|
||||
service = TestBed.get(UploadService);
|
||||
service.queue = [];
|
||||
service.activeTask = null;
|
||||
jasmine.Ajax.install();
|
||||
});
|
||||
|
||||
@@ -162,6 +157,8 @@ describe('UploadService', () => {
|
||||
});
|
||||
|
||||
it('If newVersion is set, name should be a param', () => {
|
||||
let uploadFileSpy = spyOn(service.apiService.getInstance().upload, 'uploadFile').and.callThrough();
|
||||
|
||||
let emitter = new EventEmitter();
|
||||
|
||||
const filesFake = new FileModel(<File> { name: 'fake-name', size: 10 }, {
|
||||
@@ -170,7 +167,16 @@ describe('UploadService', () => {
|
||||
service.addToQueue(filesFake);
|
||||
service.uploadFilesInTheQueue(emitter);
|
||||
|
||||
expect(jasmine.Ajax.requests.mostRecent().params.has('name')).toBe(true);
|
||||
expect(uploadFileSpy).toHaveBeenCalledWith({
|
||||
name: 'fake-name',
|
||||
size: 10
|
||||
}, undefined, undefined, null, {
|
||||
renditions: 'doclib',
|
||||
overwrite: true,
|
||||
majorVersion: undefined,
|
||||
comment: undefined,
|
||||
name: 'fake-name'
|
||||
});
|
||||
});
|
||||
|
||||
it('should use custom root folder ID given to the service', (done) => {
|
||||
|
@@ -156,9 +156,9 @@ describe('UserPreferencesService', () => {
|
||||
});
|
||||
|
||||
it('should stream only the selected attribute changes when using select', (done) => {
|
||||
preferences.disableCSRF = true;
|
||||
preferences.disableCSRF = false;
|
||||
preferences.select(UserPreferenceValues.DisableCSRF).subscribe((disableCSRFFlag) => {
|
||||
expect(disableCSRFFlag).toBeTruthy();
|
||||
expect(disableCSRFFlag).toBeFalsy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@@ -20,9 +20,9 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { AlfrescoApiService } from './alfresco-api.service';
|
||||
import { StorageService } from './storage.service';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import { OauthConfigModel } from '../models/oauth-config.model';
|
||||
|
||||
export enum UserPreferenceValues {
|
||||
PaginationSize = 'PAGINATION_SIZE',
|
||||
@@ -50,17 +50,14 @@ export class UserPreferencesService {
|
||||
* @deprecated we are grouping every value changed on the user preference in a single stream : userPreferenceValue$
|
||||
*/
|
||||
locale$: Observable<string>;
|
||||
private localeSubject: BehaviorSubject<string> ;
|
||||
private localeSubject: BehaviorSubject<string>;
|
||||
|
||||
private onChangeSubject: BehaviorSubject<any>;
|
||||
onChange: Observable<any>;
|
||||
|
||||
constructor(
|
||||
public translate: TranslateService,
|
||||
private appConfig: AppConfigService,
|
||||
private storage: StorageService,
|
||||
private apiService: AlfrescoApiService
|
||||
) {
|
||||
constructor(public translate: TranslateService,
|
||||
private appConfig: AppConfigService,
|
||||
private storage: StorageService) {
|
||||
this.appConfig.onLoad.subscribe(this.initUserPreferenceStatus.bind(this));
|
||||
this.localeSubject = new BehaviorSubject(this.userPreferenceStatus[UserPreferenceValues.Locale]);
|
||||
this.locale$ = this.localeSubject.asObservable();
|
||||
@@ -106,7 +103,9 @@ export class UserPreferencesService {
|
||||
* @param value New value for the property
|
||||
*/
|
||||
set(property: string, value: any) {
|
||||
if (!property) { return; }
|
||||
if (!property) {
|
||||
return;
|
||||
}
|
||||
this.storage.setItem(
|
||||
this.getPropertyKey(property),
|
||||
value
|
||||
@@ -149,19 +148,29 @@ export class UserPreferencesService {
|
||||
}
|
||||
|
||||
/** Authorization type (can be "ECM", "BPM" or "ALL"). */
|
||||
set authType(value: string) {
|
||||
this.storage.setItem('AUTH_TYPE', value);
|
||||
this.apiService.reset();
|
||||
/** @deprecated in 2.4.0 */
|
||||
set authType(authType: string) {
|
||||
let storedAuthType = this.storage.getItem('AUTH_TYPE');
|
||||
|
||||
if (authType !== storedAuthType) {
|
||||
this.storage.setItem('AUTH_TYPE', authType);
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated in 2.4.0 */
|
||||
get authType(): string {
|
||||
return this.storage.getItem('AUTH_TYPE') || 'ALL';
|
||||
}
|
||||
|
||||
/** Prevents the CSRF Token from being submitted if true. Only valid for Process Services. */
|
||||
set disableCSRF(value: boolean) {
|
||||
this.set('DISABLE_CSRF', value);
|
||||
this.apiService.reset();
|
||||
set disableCSRF(csrf: boolean) {
|
||||
let storedCSRF = this.storage.getItem('DISABLE_CSRF');
|
||||
|
||||
if (csrf !== null && csrf !== undefined) {
|
||||
if (csrf.toString() === storedCSRF) {
|
||||
this.set('DISABLE_CSRF', csrf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get disableCSRF(): boolean {
|
||||
@@ -196,4 +205,56 @@ export class UserPreferencesService {
|
||||
return this.appConfig.get<string>('locale') || this.translate.getBrowserLang() || 'en';
|
||||
}
|
||||
|
||||
get providers(): string {
|
||||
if (this.storage.hasItem('providers')) {
|
||||
return this.storage.getItem('providers');
|
||||
} else {
|
||||
return this.appConfig.get('providers', 'ECM');
|
||||
}
|
||||
}
|
||||
|
||||
set providers(providers: string) {
|
||||
this.storage.setItem('providers', providers);
|
||||
}
|
||||
|
||||
get bpmHost(): string {
|
||||
if (this.storage.hasItem('bpmHost')) {
|
||||
return this.storage.getItem('bpmHost');
|
||||
} else {
|
||||
return this.appConfig.get('bpmHost');
|
||||
}
|
||||
}
|
||||
|
||||
set bpmHost(bpmHost: string) {
|
||||
this.storage.setItem('bpmHost', bpmHost);
|
||||
}
|
||||
|
||||
get ecmHost(): string {
|
||||
if (this.storage.hasItem('ecmHost')) {
|
||||
return this.storage.getItem('ecmHost');
|
||||
} else {
|
||||
return this.appConfig.get('ecmHost');
|
||||
}
|
||||
}
|
||||
|
||||
set ecmHost(ecmHost: string) {
|
||||
this.storage.setItem('ecmHost', ecmHost);
|
||||
}
|
||||
|
||||
get oauthConfig(): OauthConfigModel {
|
||||
if (this.storage.hasItem('oauthConfig')) {
|
||||
return JSON.parse(this.storage.getItem('oauthConfig'));
|
||||
} else {
|
||||
return this.appConfig.get<OauthConfigModel>('oauth2');
|
||||
}
|
||||
}
|
||||
|
||||
set oauthConfig(oauthConfig: OauthConfigModel) {
|
||||
this.storage.setItem('oauthConfig', JSON.stringify(oauthConfig));
|
||||
}
|
||||
|
||||
get sso(): boolean {
|
||||
return this.providers === 'OAUTH' && this.oauthConfig.implicitFlow;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,70 +1,97 @@
|
||||
<div class="adf-setting-container">
|
||||
<div class="adf-setting-card-padding"></div>
|
||||
<mat-toolbar color="primary" class="adf-setting-toolbar">
|
||||
<h3>{{'CORE.HOST_SETTINGS.TITLE' | translate}}</h3>
|
||||
</mat-toolbar>
|
||||
|
||||
<mat-card class="adf-setting-card">
|
||||
<div *ngIf="providers==='ALL' || providers==='ECM'">
|
||||
<mat-card-header>
|
||||
<mat-card-subtitle>{{'CORE.HOST_SETTINGS.CS-HOST' | translate }}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-icon class="adf-CORE.HOST_SETTINGS-link-icon" matPrefix>link</mat-icon>
|
||||
<input matInput
|
||||
[formControl]="urlFormControlEcm"
|
||||
data-automation-id="ecmHost"
|
||||
type="text"
|
||||
(change)="onChangeECMHost($event)"
|
||||
tabindex="2"
|
||||
id="ecmHost"
|
||||
value="{{ecmHost}}"
|
||||
placeholder="http(s)://host|ip:port(/path)">
|
||||
<mat-error *ngIf="urlFormControlEcm.hasError('pattern')">
|
||||
{{ 'CORE.HOST_SETTINGS.NOT_VALID'| translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<p>
|
||||
</mat-card-content>
|
||||
</div>
|
||||
<p>
|
||||
<div *ngIf="providers==='ALL' || providers==='BPM'">
|
||||
<mat-card-header>
|
||||
<mat-card-subtitle>{{'CORE.HOST_SETTINGS.BP-HOST' | translate }}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<form id="host-form" [formGroup]="form" (submit)="onSubmit(form.value)">
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Provider" [formControl]="providers">
|
||||
<mat-option *ngFor="let provider of providersValues" [value]="provider.value">
|
||||
{{ provider.title }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-icon class="adf-CORE.HOST_SETTINGS-link-icon" matPrefix>link</mat-icon>
|
||||
<input matInput
|
||||
[formControl]="urlFormControlBpm"
|
||||
data-automation-id="bpmHost"
|
||||
type="text"
|
||||
(change)="onChangeBPMHost($event)"
|
||||
tabindex="2"
|
||||
id="bpmHost"
|
||||
value="{{bpmHost}}"
|
||||
placeholder="http(s)://host|ip:port(/path)">
|
||||
<mat-error *ngIf="urlFormControlBpm.hasError('pattern')">
|
||||
{{ 'CORE.HOST_SETTINGS.NOT_VALID'| translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</mat-card-content>
|
||||
</div>
|
||||
<mat-card-actions class="adf-CORE.HOST_SETTINGS-actions">
|
||||
<ng-container *ngIf="isALL() || isECM()">
|
||||
<mat-card-content>
|
||||
<mat-form-field class="full-width" floatLabel="{{'CORE.HOST_SETTINGS.CS-HOST' | translate }}" >
|
||||
<mat-label>{{'CORE.HOST_SETTINGS.CS-HOST' | translate }}</mat-label>
|
||||
<input matInput [formControl]="ecmHost" data-automation-id="ecmHost" type="text" tabindex="2" id="ecmHost" placeholder="http(s)://host|ip:port(/path)">
|
||||
<mat-error *ngIf="ecmHost.hasError('pattern')">
|
||||
{{ 'CORE.HOST_SETTINGS.NOT_VALID'| translate }}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="ecmHost.hasError('required')">
|
||||
{{ 'CORE.HOST_SETTINGS.REQUIRED'| translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<p>
|
||||
</mat-card-content>
|
||||
</ng-container>
|
||||
|
||||
<button mat-button onclick="window.history.back()" color="primary">
|
||||
{{'CORE.HOST_SETTINGS.BACK' | translate }}
|
||||
</button>
|
||||
<ng-container *ngIf="isALL() || isOAUTH() || isBPM()">
|
||||
<mat-card-content>
|
||||
<mat-form-field class="full-width" floatLabel="{{'CORE.HOST_SETTINGS.BP-HOST' | translate }}">
|
||||
<mat-label>{{'CORE.HOST_SETTINGS.BP-HOST' | translate }}</mat-label>
|
||||
<input matInput [formControl]="bpmHost" data-automation-id="bpmHost" type="text" tabindex="2" id="bpmHost" placeholder="http(s)://host|ip:port(/path)">
|
||||
<mat-error *ngIf="bpmHost.hasError('pattern')">
|
||||
{{ 'CORE.HOST_SETTINGS.NOT_VALID'| translate }}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="bpmHost.hasError('required')">
|
||||
{{ 'CORE.HOST_SETTINGS.REQUIRED'| translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<ng-container *ngIf="isOAUTH()">
|
||||
<div formGroupName="oauthConfig">
|
||||
<mat-form-field class="full-width" floatLabel="Auth Host">
|
||||
<mat-label>Auth Host</mat-label>
|
||||
<input matInput name="host" id="oauthHost" formControlName="host" placeholder="http(s)://host|ip:port(/path)" >
|
||||
<mat-error *ngIf="host.hasError('pattern')">
|
||||
{{ 'CORE.HOST_SETTINGS.NOT_VALID'| translate }}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="host.hasError('required')">
|
||||
{{ 'CORE.HOST_SETTINGS.REQUIRED'| translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="full-width" floatLabel="Client Id">
|
||||
<mat-label>{{ 'CORE.HOST_SETTINGS.CLIENT'| translate }}d</mat-label>
|
||||
<input matInput name="clientId" id="clientId" formControlName="clientId" placeholder="Client Id">
|
||||
<mat-error *ngIf="clientId.hasError('required')">
|
||||
{{ 'CORE.HOST_SETTINGS.REQUIRED'| translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-raised-button (click)="save($event)"
|
||||
[disabled]="urlFormControlBpm.hasError('pattern') || urlFormControlEcm.hasError('pattern')"
|
||||
color="primary">
|
||||
{{'CORE.HOST_SETTINGS.APPLY' | translate }}
|
||||
</button>
|
||||
<mat-form-field class="full-width" floatLabel="Scope">
|
||||
<mat-label>{{ 'CORE.HOST_SETTINGS.SCOPE'| translate }}</mat-label>
|
||||
<input matInput name="{{ 'CORE.HOST_SETTINGS.SCOPE'| translate }}" formControlName="scope" placeholder="Scope Id">
|
||||
<mat-error *ngIf="scope.hasError('required')">
|
||||
{{ 'CORE.HOST_SETTINGS.REQUIRED'| translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
</mat-card-actions>
|
||||
<label for="silentLogin">{{ 'CORE.HOST_SETTINGS.SILENT'| translate }}</label>
|
||||
<mat-slide-toggle class="full-width" name="silentLogin" [color]="'primary'" formControlName="silentLogin">
|
||||
</mat-slide-toggle>
|
||||
|
||||
<mat-form-field class="full-width" floatLabel="Redirect Uri">
|
||||
<mat-label>{{ 'CORE.HOST_SETTINGS.REDIRECT'| translate }}</mat-label>
|
||||
<input matInput placeholder="{{ 'CORE.HOST_SETTINGS.REDIRECT'| translate }}" name="redirectUri" formControlName="redirectUri">
|
||||
<mat-error *ngIf="redirectUri.hasError('required')">
|
||||
{{ 'CORE.HOST_SETTINGS.REQUIRED'| translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</ng-container>
|
||||
</mat-card-content>
|
||||
</ng-container>
|
||||
<mat-card-actions class="adf-actions">
|
||||
<button mat-button (click)="onCancel()" color="primary">
|
||||
{{'CORE.HOST_SETTINGS.BACK' | translate }}
|
||||
</button>
|
||||
<button type="submit" id="host-button" tabindex="4" class="adf-login-button" mat-raised-button color="primary" data-automation-id="host-button"
|
||||
[disabled]="!form.valid">
|
||||
{{'CORE.HOST_SETTINGS.APPLY' | translate }}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</form>
|
||||
</mat-card>
|
||||
<div class="adf-setting-card-padding"></div>
|
||||
</div>
|
||||
|
@@ -5,12 +5,10 @@
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
|
||||
.adf-setting-toolbar {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.adf-setting-container {
|
||||
width: 800px;
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
@@ -19,11 +19,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { HostSettingsComponent } from './host-settings.component';
|
||||
import { setupTestBed } from '../testing/setupTestBed';
|
||||
import { CoreTestingModule } from '../testing/core.testing.module';
|
||||
import { UserPreferencesService } from '../services/user-preferences.service';
|
||||
|
||||
describe('HostSettingsComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<HostSettingsComponent>;
|
||||
let component: HostSettingsComponent;
|
||||
let userPreferences: UserPreferencesService;
|
||||
let element: any;
|
||||
|
||||
setupTestBed({
|
||||
imports: [CoreTestingModule]
|
||||
@@ -32,96 +35,262 @@ describe('HostSettingsComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HostSettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
element = fixture.nativeElement;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should emit an error when the ECM url inserted is wrong', (done) => {
|
||||
fixture.detectChanges();
|
||||
describe('BPM ', () => {
|
||||
|
||||
component.error.subscribe((message: string) => {
|
||||
expect(message).toEqual('CORE.HOST_SETTING.CS_URL_ERROR');
|
||||
done();
|
||||
let ecmUrlInput;
|
||||
let bpmUrlInput;
|
||||
|
||||
beforeEach(() => {
|
||||
userPreferences.providers = 'BPM';
|
||||
fixture.detectChanges();
|
||||
bpmUrlInput = element.querySelector('#bpmHost');
|
||||
ecmUrlInput = element.querySelector('#ecmHost');
|
||||
});
|
||||
|
||||
const ecmUrlInput = fixture.nativeElement.querySelector('#ecmHost');
|
||||
ecmUrlInput.value = 'wrong_url';
|
||||
|
||||
const event: any = {};
|
||||
event.target = ecmUrlInput;
|
||||
component.onChangeECMHost(event);
|
||||
});
|
||||
|
||||
it('should emit ecmHostChange when the ECM url inserted is correct', (done) => {
|
||||
fixture.detectChanges();
|
||||
const url = 'http://localhost:9999/ecm';
|
||||
component.ecmHostChange.subscribe((message: string) => {
|
||||
expect(message).toEqual(url);
|
||||
done();
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
const ecmUrlInput = fixture.nativeElement.querySelector('#ecmHost');
|
||||
ecmUrlInput.value = url;
|
||||
it('should have a valid form when the url inserted is correct', (done) => {
|
||||
const url = 'http://localhost:9999/bpm';
|
||||
|
||||
const event: any = {};
|
||||
event.target = ecmUrlInput;
|
||||
component.onChangeECMHost(event);
|
||||
});
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('VALID');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should emit an error when the BPM url inserted is wrong', (done) => {
|
||||
fixture.detectChanges();
|
||||
component.form.valueChanges.subscribe((values) => {
|
||||
expect(values.bpmHost).toEqual(url);
|
||||
});
|
||||
|
||||
component.error.subscribe((message: string) => {
|
||||
expect(message).toEqual('CORE.HOST_SETTING.PS_URL_ERROR');
|
||||
done();
|
||||
bpmUrlInput.value = url;
|
||||
bpmUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
const bpmUrlInput: any = fixture.nativeElement.querySelector('#bpmHost');
|
||||
bpmUrlInput.value = 'wrong_url';
|
||||
it('should have an invalid form when the inserted is wrong', (done) => {
|
||||
const url = 'wrong';
|
||||
|
||||
const event: any = {};
|
||||
event.target = bpmUrlInput;
|
||||
component.onChangeBPMHost(event);
|
||||
});
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('INVALID');
|
||||
expect(component.bpmHost.hasError('pattern')).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
|
||||
it('should emit bpmHostChange when the BPM url inserted is correct', (done) => {
|
||||
fixture.detectChanges();
|
||||
const url = 'http://localhost:9999/bpm';
|
||||
|
||||
component.ecmHostChange.subscribe((message: string) => {
|
||||
expect(message).toEqual(url);
|
||||
done();
|
||||
bpmUrlInput.value = url;
|
||||
bpmUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
const ecmUrlInput = fixture.nativeElement.querySelector('#bpmHost');
|
||||
ecmUrlInput.value = url;
|
||||
it('should not render the ECM url config if setting provider is BPM', () => {
|
||||
expect(ecmUrlInput).toEqual(null);
|
||||
expect(bpmUrlInput).toBeDefined();
|
||||
});
|
||||
|
||||
const event: any = {};
|
||||
event.target = ecmUrlInput;
|
||||
component.onChangeECMHost(event);
|
||||
});
|
||||
|
||||
it('should not render the ECM url config if setting provider is BPM', () => {
|
||||
component.providers = 'BPM';
|
||||
describe('ECM ', () => {
|
||||
|
||||
fixture.detectChanges();
|
||||
let ecmUrlInput;
|
||||
let bpmUrlInput;
|
||||
|
||||
const bpmUrlInput = fixture.nativeElement.querySelector('#bpmHost');
|
||||
const ecmUrlInput = fixture.nativeElement.querySelector('#ecmHost');
|
||||
expect(ecmUrlInput).toEqual(null);
|
||||
expect(bpmUrlInput).toBeDefined();
|
||||
beforeEach(() => {
|
||||
userPreferences.providers = 'ECM';
|
||||
fixture.detectChanges();
|
||||
bpmUrlInput = element.querySelector('#bpmHost');
|
||||
ecmUrlInput = element.querySelector('#ecmHost');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should have a valid form when the url inserted is correct', (done) => {
|
||||
const url = 'http://localhost:9999/ecm';
|
||||
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('VALID');
|
||||
done();
|
||||
});
|
||||
|
||||
ecmUrlInput.value = url;
|
||||
ecmUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
it('should have an invalid form when the url inserted is wrong', (done) => {
|
||||
const url = 'wrong';
|
||||
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('INVALID');
|
||||
expect(component.ecmHost.hasError('pattern')).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
|
||||
ecmUrlInput.value = url;
|
||||
ecmUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
it('should not render the BPM url config if setting provider is BPM', () => {
|
||||
expect(bpmUrlInput).toEqual(null);
|
||||
expect(ecmUrlInput).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should hide the BPM url config if setting provider is ECM', () => {
|
||||
component.providers = 'ECM';
|
||||
describe('ALL ', () => {
|
||||
|
||||
fixture.detectChanges();
|
||||
let ecmUrlInput;
|
||||
let bpmUrlInput;
|
||||
|
||||
beforeEach(() => {
|
||||
userPreferences.providers = 'ALL';
|
||||
fixture.detectChanges();
|
||||
bpmUrlInput = element.querySelector('#bpmHost');
|
||||
ecmUrlInput = element.querySelector('#ecmHost');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should have a valid form when the BPM and ECM url inserted are correct', (done) => {
|
||||
const urlEcm = 'http://localhost:9999/ecm';
|
||||
const urlBpm = 'http://localhost:9999/bpm';
|
||||
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('VALID');
|
||||
done();
|
||||
});
|
||||
|
||||
ecmUrlInput.value = urlEcm;
|
||||
bpmUrlInput.value = urlBpm;
|
||||
ecmUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
it('should have an invalid form when one of the ECM url inserted is wrong', (done) => {
|
||||
const url = 'wrong';
|
||||
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('INVALID');
|
||||
expect(component.ecmHost.hasError('pattern')).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
|
||||
ecmUrlInput.value = url;
|
||||
ecmUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
it('should have an invalid form when one of the BPM url inserted is wrong', (done) => {
|
||||
const url = 'wrong';
|
||||
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('INVALID');
|
||||
expect(component.bpmHost.hasError('pattern')).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
|
||||
bpmUrlInput.value = url;
|
||||
bpmUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
it('should have an invalid form when both BPM and ECM url inserted are wrong', (done) => {
|
||||
const url = 'wrong';
|
||||
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('INVALID');
|
||||
expect(component.bpmHost.hasError('pattern')).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
|
||||
bpmUrlInput.value = url;
|
||||
ecmUrlInput.value = url;
|
||||
bpmUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
const ecmUrlInput = fixture.nativeElement.querySelector('#ecmHost');
|
||||
const bpmUrlInput = fixture.nativeElement.querySelector('#bpmHost');
|
||||
expect(bpmUrlInput).toEqual(null);
|
||||
expect(ecmUrlInput).toBeDefined();
|
||||
});
|
||||
|
||||
describe('OAUTH ', () => {
|
||||
|
||||
let ecmUrlInput;
|
||||
let bpmUrlInput;
|
||||
let oauthHostUrlInput;
|
||||
let clientIdInput;
|
||||
|
||||
beforeEach(() => {
|
||||
userPreferences.providers = 'OAUTH';
|
||||
userPreferences.oauthConfig = {
|
||||
host: 'http://localhost:6543',
|
||||
redirectUri: '/',
|
||||
silentLogin: false,
|
||||
implicitFlow: true,
|
||||
clientId: 'activiti',
|
||||
scope: 'openid',
|
||||
secret: ''
|
||||
};
|
||||
fixture.detectChanges();
|
||||
bpmUrlInput = element.querySelector('#bpmHost');
|
||||
oauthHostUrlInput = element.querySelector('#oauthHost');
|
||||
clientIdInput = element.querySelector('#clientId');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should have a valid form when the BPM is correct', (done) => {
|
||||
const urlBpm = 'http://localhost:9999/bpm';
|
||||
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('VALID');
|
||||
done();
|
||||
});
|
||||
|
||||
bpmUrlInput.value = urlBpm;
|
||||
bpmUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
it('should have an invalid form when the url inserted is wrong', (done) => {
|
||||
const url = 'wrong';
|
||||
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('INVALID');
|
||||
expect(component.bpmHost.hasError('pattern')).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
|
||||
bpmUrlInput.value = url;
|
||||
bpmUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
it('should have an invalid form when the host is wrong', (done) => {
|
||||
const hostUrl = 'wrong';
|
||||
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('INVALID');
|
||||
expect(component.host.hasError('pattern')).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
|
||||
oauthHostUrlInput.value = hostUrl;
|
||||
oauthHostUrlInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
it('should have a required clientId an invalid form when the clientId is missing', (done) => {
|
||||
component.form.statusChanges.subscribe((status: string) => {
|
||||
expect(status).toEqual('INVALID');
|
||||
expect(component.clientId.hasError('required')).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
|
||||
clientIdInput.value = '';
|
||||
clientIdInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -15,12 +15,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { FormControl, Validators } from '@angular/forms';
|
||||
import { LogService } from '../services/log.service';
|
||||
import { SettingsService } from '../services/settings.service';
|
||||
import { StorageService } from '../services/storage.service';
|
||||
import { TranslationService } from '../services/translation.service';
|
||||
import { Component, EventEmitter, Output, ViewEncapsulation, OnInit } from '@angular/core';
|
||||
import { Validators, FormGroup, FormBuilder, AbstractControl } from '@angular/forms';
|
||||
import { UserPreferencesService } from '../services';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-host-settings',
|
||||
@@ -31,20 +28,18 @@ import { TranslationService } from '../services/translation.service';
|
||||
styleUrls: ['host-settings.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class HostSettingsComponent {
|
||||
export class HostSettingsComponent implements OnInit {
|
||||
|
||||
HOST_REGEX: string = '^(http|https):\/\/.*[^/]$';
|
||||
|
||||
ecmHost: string;
|
||||
ecmHostTmp: string;
|
||||
bpmHost: string;
|
||||
bpmHostTmp: string;
|
||||
urlFormControlEcm = new FormControl('', [Validators.required, Validators.pattern(this.HOST_REGEX)]);
|
||||
urlFormControlBpm = new FormControl('', [Validators.required, Validators.pattern(this.HOST_REGEX)]);
|
||||
providersValues = [
|
||||
{ title: 'ECM and BPM', value: 'ALL' },
|
||||
{ title: 'BPM', value: 'BPM' },
|
||||
{ title: 'ECM', value: 'ECM' },
|
||||
{ title: 'OAUTH', value: 'OAUTH' }
|
||||
];
|
||||
|
||||
/** Determines which configurations are shown. Possible valid values are "ECM", "BPM" or "ALL". */
|
||||
@Input()
|
||||
providers: string = 'ALL';
|
||||
form: FormGroup;
|
||||
|
||||
/** Emitted when the URL is invalid. */
|
||||
@Output()
|
||||
@@ -54,56 +49,136 @@ export class HostSettingsComponent {
|
||||
@Output()
|
||||
ecmHostChange = new EventEmitter<string>();
|
||||
|
||||
@Output()
|
||||
cancel = new EventEmitter<boolean>();
|
||||
|
||||
@Output()
|
||||
success = new EventEmitter<boolean>();
|
||||
|
||||
/** Emitted when the bpm host URL is changed. */
|
||||
@Output()
|
||||
bpmHostChange = new EventEmitter<string>();
|
||||
|
||||
constructor(private settingsService: SettingsService,
|
||||
private storage: StorageService,
|
||||
private logService: LogService,
|
||||
private translationService: TranslationService) {
|
||||
this.ecmHostTmp = this.ecmHost = storage.getItem('ecmHost') || this.settingsService.ecmHost;
|
||||
this.bpmHostTmp = this.bpmHost = storage.getItem('bpmHost') || this.settingsService.bpmHost;
|
||||
constructor(private fb: FormBuilder,
|
||||
private userPreference: UserPreferencesService) {
|
||||
}
|
||||
|
||||
public onChangeECMHost(event: any): void {
|
||||
let value = (<HTMLInputElement> event.target).value.trim();
|
||||
if (value && this.isValidUrl(value)) {
|
||||
this.logService.info(`ECM host: ${value}`);
|
||||
this.ecmHostTmp = value;
|
||||
this.ecmHostChange.emit(value);
|
||||
} else {
|
||||
this.translationService.get('CORE.HOST_SETTING.CS_URL_ERROR').subscribe((message) => {
|
||||
this.error.emit(message);
|
||||
ngOnInit() {
|
||||
|
||||
let providerSelected = this.userPreference.providers;
|
||||
|
||||
this.form = this.fb.group({
|
||||
providers: [providerSelected, Validators.required],
|
||||
ecmHost: [this.userPreference.ecmHost, [Validators.required, Validators.pattern(this.HOST_REGEX)]],
|
||||
bpmHost: [this.userPreference.bpmHost, [Validators.required, Validators.pattern(this.HOST_REGEX)]]
|
||||
});
|
||||
|
||||
const oAuthConfig = this.userPreference.oauthConfig;
|
||||
if (oAuthConfig) {
|
||||
const oauthGroup = this.fb.group( {
|
||||
host: [oAuthConfig.host, [Validators.required, Validators.pattern(this.HOST_REGEX)]],
|
||||
clientId: [oAuthConfig.clientId, Validators.required],
|
||||
redirectUri: [oAuthConfig.redirectUri, Validators.required],
|
||||
scope: [oAuthConfig.scope, Validators.required],
|
||||
secret: oAuthConfig.secret,
|
||||
silentLogin: oAuthConfig.silentLogin,
|
||||
implicitFlow: oAuthConfig.implicitFlow
|
||||
});
|
||||
this.form.addControl('oauthConfig', oauthGroup);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public onChangeBPMHost(event: any): void {
|
||||
let value = (<HTMLInputElement> event.target).value.trim();
|
||||
if (value && this.isValidUrl(value)) {
|
||||
this.logService.info(`BPM host: ${value}`);
|
||||
this.bpmHostTmp = value;
|
||||
this.bpmHostChange.emit(value);
|
||||
} else {
|
||||
this.translationService.get('CORE.HOST_SETTING.PS_URL_ERROR').subscribe((message) => {
|
||||
this.error.emit(message);
|
||||
});
|
||||
}
|
||||
onCancel() {
|
||||
this.cancel.emit(true);
|
||||
}
|
||||
|
||||
public save(event: KeyboardEvent): void {
|
||||
if (this.bpmHost !== this.bpmHostTmp) {
|
||||
this.storage.setItem(`bpmHost`, this.bpmHostTmp);
|
||||
onSubmit(values: any) {
|
||||
this.userPreference.providers = values.providers;
|
||||
if (this.isBPM()) {
|
||||
this.saveBPMValues(values);
|
||||
} else if (this.isECM()) {
|
||||
this.saveECMValues(values);
|
||||
} else if (this.isALL()) {
|
||||
this.saveECMValues(values);
|
||||
this.saveBPMValues(values);
|
||||
} else if (this.isOAUTH()) {
|
||||
this.saveOAuthValues(values);
|
||||
}
|
||||
if (this.ecmHost !== this.ecmHostTmp) {
|
||||
this.storage.setItem(`ecmHost`, this.ecmHostTmp);
|
||||
}
|
||||
window.location.href = '/';
|
||||
this.success.emit(true);
|
||||
}
|
||||
|
||||
isValidUrl(url: string) {
|
||||
return /^(http|https):\/\/.*/.test(url);
|
||||
saveOAuthValues(values: any) {
|
||||
this.userPreference.oauthConfig = values.oauthConfig;
|
||||
this.userPreference.bpmHost = values.bpmHost;
|
||||
}
|
||||
|
||||
saveBPMValues(values: any) {
|
||||
this.userPreference.bpmHost = values.bpmHost;
|
||||
}
|
||||
|
||||
saveECMValues(values: any) {
|
||||
this.userPreference.ecmHost = values.ecmHost;
|
||||
}
|
||||
|
||||
isBPM(): boolean {
|
||||
return this.providers.value === 'BPM';
|
||||
}
|
||||
|
||||
isECM(): boolean {
|
||||
return this.providers.value === 'ECM';
|
||||
}
|
||||
|
||||
isALL(): boolean {
|
||||
return this.providers.value === 'ALL';
|
||||
}
|
||||
|
||||
isOAUTH(): boolean {
|
||||
return this.providers.value === 'OAUTH';
|
||||
}
|
||||
|
||||
get providers(): AbstractControl {
|
||||
return this.form.get('providers');
|
||||
}
|
||||
|
||||
get bpmHost(): AbstractControl {
|
||||
return this.form.get('bpmHost');
|
||||
}
|
||||
|
||||
get ecmHost(): AbstractControl {
|
||||
return this.form.get('ecmHost');
|
||||
}
|
||||
|
||||
get host(): AbstractControl {
|
||||
return this.oauthConfig.get('host');
|
||||
}
|
||||
|
||||
get clientId(): AbstractControl {
|
||||
return this.oauthConfig.get('clientId');
|
||||
}
|
||||
|
||||
get scope(): AbstractControl {
|
||||
return this.oauthConfig.get('scope');
|
||||
}
|
||||
|
||||
get secretId(): AbstractControl {
|
||||
return this.oauthConfig.get('secretId');
|
||||
}
|
||||
|
||||
get implicitFlow(): AbstractControl {
|
||||
return this.oauthConfig.get('implicitFlow');
|
||||
}
|
||||
|
||||
get silentLogin(): AbstractControl {
|
||||
return this.oauthConfig.get('silentLogin');
|
||||
}
|
||||
|
||||
get redirectUri(): AbstractControl {
|
||||
return this.oauthConfig.get('redirectUri');
|
||||
}
|
||||
|
||||
get oauthConfig(): AbstractControl {
|
||||
return this.form.get('oauthConfig');
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -36,6 +36,8 @@ export const setupTestBed = (moduleDef: TestModuleMetadata) => {
|
||||
preventAngularFromResetting();
|
||||
TestBed.configureTestingModule(moduleDef);
|
||||
await TestBed.compileComponents();
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
|
||||
// prevent Angular from resetting testing module
|
||||
TestBed.resetTestingModule = () => TestBed;
|
||||
|
Reference in New Issue
Block a user