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
@@ -6,6 +6,16 @@
|
||||
"PROPERTIES": "Properties",
|
||||
"VERSIONS": "Versions"
|
||||
},
|
||||
"HOME": {
|
||||
"TITLE": "Angular components for Alfresco",
|
||||
"DOCUMENTATION": "Documentation"
|
||||
},
|
||||
"LOGOUT": {
|
||||
"TITLE": "Logout Page",
|
||||
"SUB_TITLE": "You are now logged out",
|
||||
"LOGIN": "Login",
|
||||
"HOME": "Home"
|
||||
},
|
||||
"ADF_VERSION_MANAGER": {
|
||||
"ALLOW_DELETE": "Allow delete",
|
||||
"SHOW_COMMENTS" : "Show comments on versions",
|
||||
|
@@ -2,7 +2,18 @@
|
||||
"$schema": "../../lib/core/app-config/schema.json",
|
||||
"ecmHost": "http://{hostname}:{port}",
|
||||
"bpmHost": "http://{hostname}:{port}",
|
||||
"providers": "OAUTH",
|
||||
"contextRootBpm": "activiti-app",
|
||||
"oauth2": {
|
||||
"host": "YOUR_AUTH_SERVER",
|
||||
"clientId": "activiti",
|
||||
"scope": "openid",
|
||||
"secret": "",
|
||||
"implicitFlow": true,
|
||||
"silentLogin": true,
|
||||
"redirectUri": "/",
|
||||
"redirectUriLogout": "/logout"
|
||||
},
|
||||
"application": {
|
||||
"name": "Alfresco ADF Application",
|
||||
"copyright": "© 2016 - 2018 Alfresco Software, Inc. All Rights Reserved."
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
|
||||
import { AlfrescoApiService, SettingsService, PageTitleService, StorageService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiService, PageTitleService } from '@alfresco/adf-core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
@@ -27,15 +27,12 @@ import { Router } from '@angular/router';
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
|
||||
constructor(private settingsService: SettingsService,
|
||||
private storage: StorageService,
|
||||
private pageTitleService: PageTitleService,
|
||||
constructor(private pageTitleService: PageTitleService,
|
||||
private alfrescoApiService: AlfrescoApiService,
|
||||
private router: Router) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.setProvider();
|
||||
|
||||
this.pageTitleService.setTitle('title');
|
||||
|
||||
@@ -46,10 +43,4 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private setProvider() {
|
||||
if (this.storage.hasItem(`providers`)) {
|
||||
this.settingsService.setProviders(this.storage.getItem(`providers`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import { AppComponent } from './app.component';
|
||||
import { AdfModule } from './adf.module';
|
||||
import { MaterialModule } from './material.module';
|
||||
import { LoginComponent } from './components/login/login.component';
|
||||
import { LogoutComponent } from './components/logout/logout.component';
|
||||
import { SettingsComponent } from './components/settings/settings.component';
|
||||
import { AppLayoutComponent } from './components/app-layout/app-layout.component';
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
@@ -71,6 +72,7 @@ import { NotificationsComponent } from './components/notifications/notifications
|
||||
declarations: [
|
||||
AppComponent,
|
||||
LoginComponent,
|
||||
LogoutComponent,
|
||||
SettingsComponent,
|
||||
AppLayoutComponent,
|
||||
HomeComponent,
|
||||
|
@@ -17,11 +17,12 @@
|
||||
|
||||
import { ModuleWithProviders } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { AuthGuard, AuthGuardBpm, AuthGuardEcm, ErrorContentComponent } from '@alfresco/adf-core';
|
||||
import { AuthGuard, AuthGuardEcm, ErrorContentComponent, AuthGuardBpm } from '@alfresco/adf-core';
|
||||
import { AppLayoutComponent } from './components/app-layout/app-layout.component';
|
||||
import { LoginComponent } from './components/login/login.component';
|
||||
import { SettingsComponent } from './components/settings/settings.component';
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
import { LogoutComponent } from './components/logout/logout.component';
|
||||
import { AboutComponent } from './components/about/about.component';
|
||||
import { ProcessServiceComponent } from './components/process-service/process-service.component';
|
||||
import { ShowDiagramComponent } from './components/process-service/show-diagram.component';
|
||||
@@ -52,8 +53,9 @@ import { NotificationsComponent } from './components/notifications/notifications
|
||||
|
||||
export const appRoutes: Routes = [
|
||||
{ path: 'login', component: LoginComponent },
|
||||
{ path: 'logout', component: LogoutComponent },
|
||||
{ path: 'settings', component: SettingsComponent },
|
||||
{ path: 'files/:nodeId/view', component: FileViewComponent, canActivate: [AuthGuardEcm], outlet: 'overlay' },
|
||||
{ path: 'files/:nodeId/view', component: FileViewComponent, canActivate: [ AuthGuardEcm ], outlet: 'overlay' },
|
||||
{ path: 'preview/blob', component: BlobPreviewComponent, outlet: 'overlay', pathMatch: 'full' },
|
||||
{ path: 'preview/s/:id', component: SharedLinkViewComponent },
|
||||
{
|
||||
@@ -226,4 +228,4 @@ export const appRoutes: Routes = [
|
||||
}
|
||||
];
|
||||
|
||||
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
|
||||
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes, { initialNavigation: true });
|
||||
|
@@ -37,7 +37,7 @@
|
||||
<mat-icon matListIcon class="sidenav-menu-icon">{{link.icon}}</mat-icon>
|
||||
<div class="sidenav-menu-label" *ngIf="!isMenuMinimized()">{{link.title | translate }}</div>
|
||||
</a>
|
||||
<a mat-list-item adf-logout class="adf-sidenav-link">
|
||||
<a mat-list-item adf-logout [enabelRedirect]="enabelRedirect" redirectUri="/logout" class="adf-sidenav-link">
|
||||
<mat-icon matListIcon class="sidenav-menu-icon">exit_to_app</mat-icon>
|
||||
<div class="sidenav-menu-label" *ngIf="!isMenuMinimized()">Logout</div>
|
||||
</a>
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
|
||||
import { UserPreferencesService, AppConfigService } from '@alfresco/adf-core';
|
||||
import { UserPreferencesService, AppConfigService, AlfrescoApiService } from '@alfresco/adf-core';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'app-layout.component.html',
|
||||
@@ -54,18 +54,23 @@ export class AppLayoutComponent implements OnInit {
|
||||
|
||||
expandedSidenav = false;
|
||||
|
||||
enabelRedirect = true;
|
||||
|
||||
ngOnInit() {
|
||||
const expand = this.config.get<boolean>('sideNav.expandedSidenav');
|
||||
const preserveState = this.config.get('sideNav.preserveState');
|
||||
|
||||
if (preserveState && expand) {
|
||||
if (preserveState && expand) {
|
||||
this.expandedSidenav = (this.userpreference.get('expandedSidenav', expand.toString()) === 'true');
|
||||
} else if (expand) {
|
||||
this.expandedSidenav = expand;
|
||||
}
|
||||
}
|
||||
constructor( private userpreference: UserPreferencesService, private config: AppConfigService) {
|
||||
|
||||
constructor(private userpreference: UserPreferencesService, private config: AppConfigService, private alfrescoApiService: AlfrescoApiService) {
|
||||
if (this.alfrescoApiService.getInstance().isOauthConfiguration()) {
|
||||
this.enabelRedirect = false;
|
||||
}
|
||||
}
|
||||
|
||||
setState(state) {
|
||||
|
@@ -25,7 +25,7 @@ import { MatDialog } from '@angular/material';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { MinimalNodeEntity, NodePaging, Pagination, MinimalNodeEntryEntity, SiteEntry } from 'alfresco-js-api';
|
||||
import {
|
||||
AuthenticationService, AppConfigService, ContentService, TranslationService,
|
||||
AuthenticationService, ContentService, TranslationService,
|
||||
FileUploadEvent, FolderCreatedEvent, LogService, NotificationService,
|
||||
UploadService, DataColumn, DataRow, UserPreferencesService,
|
||||
PaginationComponent, FormValues, DisplayMode, UserPreferenceValues, InfinitePaginationComponent
|
||||
@@ -56,7 +56,7 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
|
||||
displayMode = DisplayMode.List;
|
||||
includeFields = ['isFavorite', 'isLocked', 'aspectNames'];
|
||||
|
||||
baseShareUrl = this.appConfig.get<string>('ecmHost') + '/preview/s/';
|
||||
baseShareUrl = this.preference.ecmHost + '/preview/s/';
|
||||
|
||||
toolbarColor = 'default';
|
||||
|
||||
@@ -191,7 +191,6 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
|
||||
private router: Router,
|
||||
private logService: LogService,
|
||||
private preference: UserPreferencesService,
|
||||
private appConfig: AppConfigService,
|
||||
private preview: PreviewService,
|
||||
@Optional() private route: ActivatedRoute,
|
||||
public authenticationService: AuthenticationService) {
|
||||
|
@@ -2,10 +2,10 @@
|
||||
<div class="adf-home-section ad">
|
||||
<div class="adf-home-headline">
|
||||
<h1 class="mat-h1">ADF</h1>
|
||||
<h2> Angular components for Alfresco</h2>
|
||||
<h2>{{ 'APP.HOME.TITLE' | translate}}</h2>
|
||||
</div>
|
||||
<div class="adf-home-start">
|
||||
<a mat-raised-button class="adf-home-docs-button adf-primary-color" href="https://github.com/Alfresco/alfresco-ng2-components/tree/master/docs">DOCS</a>
|
||||
<a mat-raised-button class="adf-home-docs-button adf-primary-color" href="https://github.com/Alfresco/alfresco-ng2-components/tree/master/docs">{{ 'APP.HOME.DOCUMENTATION' | translate}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
@@ -1,24 +1,6 @@
|
||||
<!--BPM, ECN AND CSRF TOGGLE-->
|
||||
|
||||
<div class="settings">
|
||||
<p class="toggle">
|
||||
<mat-slide-toggle
|
||||
id="switch1"
|
||||
[color]="'primary'"
|
||||
(change)="toggleECM()"
|
||||
[checked]="isECM">
|
||||
{{ 'LOGIN.CONTENT_SERVICES'| translate }}
|
||||
</mat-slide-toggle>
|
||||
</p>
|
||||
<p class="toggle">
|
||||
<mat-slide-toggle
|
||||
id="switch2"
|
||||
[color]="'primary'"
|
||||
(change)="toggleBPM()"
|
||||
[checked]="isBPM">
|
||||
{{ 'LOGIN.PROCESS_SERVICES'| translate }}
|
||||
</mat-slide-toggle>
|
||||
</p>
|
||||
<p class="toggle">
|
||||
<mat-slide-toggle
|
||||
id="switch3"
|
||||
@@ -57,24 +39,6 @@
|
||||
(success)="onLogin($event)"
|
||||
(error)="onError($event)">
|
||||
<div class="mobile-settings">
|
||||
<p>
|
||||
<mat-slide-toggle
|
||||
id="switch1-mobile"
|
||||
[color]="'primary'"
|
||||
(change)="toggleECM()"
|
||||
[checked]="isECM">
|
||||
{{ 'LOGIN.CONTENT_SERVICES'| translate }}
|
||||
</mat-slide-toggle>
|
||||
</p>
|
||||
<p>
|
||||
<mat-slide-toggle
|
||||
id="switch2-mobile"
|
||||
[color]="'primary'"
|
||||
(change)="toggleBPM()"
|
||||
[checked]="isBPM">
|
||||
{{ 'LOGIN.PROCESS_SERVICES'| translate }}
|
||||
</mat-slide-toggle>
|
||||
</p>
|
||||
<p>
|
||||
<mat-slide-toggle
|
||||
id="switch3-mobile"
|
||||
|
@@ -18,7 +18,7 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { LogService, StorageService } from '@alfresco/adf-core';
|
||||
import { LogService, UserPreferencesService } from '@alfresco/adf-core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
@@ -34,13 +34,11 @@ export class LoginComponent implements OnInit {
|
||||
customValidation: any;
|
||||
|
||||
disableCsrf = false;
|
||||
isECM = true;
|
||||
isBPM = false;
|
||||
showFooter = true;
|
||||
customMinLength = 2;
|
||||
|
||||
constructor(private router: Router,
|
||||
private storage: StorageService,
|
||||
private userPreference: UserPreferencesService,
|
||||
private logService: LogService) {
|
||||
this.customValidation = {
|
||||
username: ['', Validators.compose([Validators.required, Validators.minLength(this.customMinLength)])],
|
||||
@@ -53,24 +51,11 @@ export class LoginComponent implements OnInit {
|
||||
this.alfrescologin.addCustomValidationError('username', 'minlength', 'LOGIN.MESSAGES.USERNAME-MIN', {minLength: this.customMinLength});
|
||||
this.alfrescologin.addCustomValidationError('password', 'required', 'LOGIN.MESSAGES.PASSWORD-REQUIRED');
|
||||
|
||||
if (this.storage.hasItem('providers')) {
|
||||
this.providers = this.storage.getItem('providers');
|
||||
}
|
||||
|
||||
this.initProviders();
|
||||
}
|
||||
|
||||
initProviders() {
|
||||
if (this.providers === 'BPM') {
|
||||
this.isECM = false;
|
||||
this.isBPM = true;
|
||||
} else if (this.providers === 'ECM') {
|
||||
this.isECM = true;
|
||||
this.isBPM = false;
|
||||
} else if (this.providers === 'ALL') {
|
||||
this.isECM = true;
|
||||
this.isBPM = true;
|
||||
}
|
||||
this.providers = this.userPreference.providers;
|
||||
}
|
||||
|
||||
onLogin($event) {
|
||||
@@ -81,16 +66,6 @@ export class LoginComponent implements OnInit {
|
||||
this.logService.error($event);
|
||||
}
|
||||
|
||||
toggleECM() {
|
||||
this.isECM = !this.isECM;
|
||||
this.storage.setItem('providers', this.updateProvider());
|
||||
}
|
||||
|
||||
toggleBPM() {
|
||||
this.isBPM = !this.isBPM;
|
||||
this.storage.setItem('providers', this.updateProvider());
|
||||
}
|
||||
|
||||
toggleCSRF() {
|
||||
this.disableCsrf = !this.disableCsrf;
|
||||
}
|
||||
@@ -99,26 +74,6 @@ export class LoginComponent implements OnInit {
|
||||
this.showFooter = !this.showFooter;
|
||||
}
|
||||
|
||||
updateProvider() {
|
||||
if (this.isBPM && this.isECM) {
|
||||
this.providers = 'ALL';
|
||||
return this.providers;
|
||||
}
|
||||
|
||||
if (this.isECM) {
|
||||
this.providers = 'ECM';
|
||||
return this.providers;
|
||||
}
|
||||
|
||||
if (this.isBPM) {
|
||||
this.providers = 'BPM';
|
||||
return this.providers;
|
||||
}
|
||||
|
||||
this.providers = '';
|
||||
return this.providers;
|
||||
}
|
||||
|
||||
checkForm(event: any) {
|
||||
const values = event.values;
|
||||
this.logService.log(values);
|
||||
|
17
demo-shell/src/app/components/logout/logout.component.html
Normal file
17
demo-shell/src/app/components/logout/logout.component.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<header class="adf-logout-background">
|
||||
<div class="adf-logout-section">
|
||||
<div class="adf-logout-headline">
|
||||
<h1>{{ 'APP.LOGOUT.TITLE' | translate}}</h1>
|
||||
<h2>{{ 'APP.LOGOUT.SUB_TITLE' | translate}}</h2>
|
||||
</div>
|
||||
|
||||
<div class="adf-logout-login">
|
||||
<a mat-raised-button class="adf-logout-docs-button adf-primary-color"
|
||||
href="/login">{{ 'APP.LOGOUT.LOGIN' | translate}}</a>
|
||||
</div>
|
||||
<div class="adf-logout-home">
|
||||
<a mat-raised-button class="adf-logout-docs-button adf-primary-color"
|
||||
href="/">{{ 'APP.LOGOUT.HOME' | translate}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
39
demo-shell/src/app/components/logout/logout.component.scss
Normal file
39
demo-shell/src/app/components/logout/logout.component.scss
Normal file
@@ -0,0 +1,39 @@
|
||||
:host {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.adf-logout-header-background {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.adf-logout-section {
|
||||
text-align: center;
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
.adf-logout-headline {
|
||||
|
||||
h1 {
|
||||
font-size: 56px;
|
||||
font-weight: 300;
|
||||
line-height: 56px;
|
||||
margin: 15px 5px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
line-height: 28px;
|
||||
margin: 15px 0 25px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-logout-docs-button {
|
||||
margin: 30px;
|
||||
}
|
||||
|
||||
.adf-logout-login {
|
||||
float: left;
|
||||
}
|
25
demo-shell/src/app/components/logout/logout.component.ts
Normal file
25
demo-shell/src/app/components/logout/logout.component.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*!
|
||||
* @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.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-logout',
|
||||
templateUrl: './logout.component.html',
|
||||
styleUrls: ['./logout.component.scss']
|
||||
})
|
||||
export class LogoutComponent {}
|
@@ -1 +1 @@
|
||||
<adf-host-settings (error)="onError($event)"></adf-host-settings>
|
||||
<adf-host-settings (cancel)="onCancel($event)" (success)="onSuccess($event)" (error)="onError($event)"></adf-host-settings>
|
||||
|
@@ -16,7 +16,8 @@
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { LogService } from '@alfresco/adf-core';
|
||||
import { LogService, AuthenticationService, AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
@@ -24,10 +25,23 @@ import { LogService } from '@alfresco/adf-core';
|
||||
})
|
||||
export class SettingsComponent {
|
||||
|
||||
constructor(public logService: LogService) {
|
||||
constructor(private router: Router,
|
||||
private authService: AuthenticationService,
|
||||
private alfrescoApiService: AlfrescoApiService,
|
||||
public logService: LogService) {
|
||||
}
|
||||
|
||||
onError(error: string) {
|
||||
this.logService.log(error);
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
onSuccess() {
|
||||
this.authService.removeTicket();
|
||||
this.alfrescoApiService.reset();
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,14 @@ Logs the user out when the decorated element is clicked.
|
||||
<button adf-logout>Logout</button>
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| -- | -- | -- | -- |
|
||||
| redirectUri | `string` | 'login' | Uri to be redirect after the logout |
|
||||
| enabelRedirect | `boolean` | 'true' | enable/disable redirect after logout |
|
||||
|
||||
|
||||
## See also
|
||||
|
||||
- [Login component](login.component.md)
|
||||
|
@@ -19,6 +19,7 @@ let button = new MenuButton({
|
||||
isVisible: this.isItemValid.bind(this)
|
||||
});
|
||||
```
|
||||
## Class members
|
||||
|
||||
## Properties
|
||||
|
||||
|
@@ -42,6 +42,8 @@ export class FolderDialogComponent implements OnInit {
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
/** Emitted when the edit/create folder is successfully created/mmodified
|
||||
*/
|
||||
@Output()
|
||||
success: EventEmitter<any> = new EventEmitter<MinimalNodeEntryEntity>();
|
||||
|
||||
|
@@ -20,6 +20,9 @@ import {
|
||||
AppConfigService,
|
||||
ContentService,
|
||||
StorageService,
|
||||
UserPreferencesService,
|
||||
setupTestBed,
|
||||
CoreModule,
|
||||
TranslationMock
|
||||
} from '@alfresco/adf-core';
|
||||
import { FileNode, FolderNode } from '../../mock';
|
||||
@@ -28,16 +31,25 @@ import { DocumentActionsService } from './document-actions.service';
|
||||
import { DocumentListService } from './document-list.service';
|
||||
import { NodeActionsService } from './node-actions.service';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
describe('DocumentActionsService', () => {
|
||||
|
||||
let service: DocumentActionsService;
|
||||
let documentListService: DocumentListService;
|
||||
let nodeActionsService: NodeActionsService;
|
||||
let userPreferences;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
let contentService = new ContentService(null, null, null, null);
|
||||
let alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
|
||||
let alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), userPreferences, new StorageService());
|
||||
|
||||
documentListService = new DocumentListService(null, contentService, alfrescoApiService, null, null);
|
||||
service = new DocumentActionsService(null, null, new TranslationMock(), documentListService, contentService);
|
||||
|
@@ -16,8 +16,9 @@
|
||||
*/
|
||||
|
||||
import { AlfrescoApiServiceMock, AlfrescoApiService,
|
||||
AppConfigService, StorageService, ContentService } from '@alfresco/adf-core';
|
||||
AppConfigService, StorageService, ContentService, UserPreferencesService, setupTestBed, CoreModule } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from './document-list.service';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
@@ -25,6 +26,7 @@ describe('DocumentListService', () => {
|
||||
|
||||
let service: DocumentListService;
|
||||
let alfrescoApiService: AlfrescoApiService;
|
||||
let userPreferences: UserPreferencesService;
|
||||
|
||||
let fakeEntryNode = {
|
||||
'entry': {
|
||||
@@ -87,9 +89,17 @@ describe('DocumentListService', () => {
|
||||
}
|
||||
};
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
|
||||
let contentService = new ContentService(null, null, null, null);
|
||||
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
|
||||
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), userPreferences, new StorageService());
|
||||
service = new DocumentListService(null, contentService, alfrescoApiService, null, null);
|
||||
jasmine.Ajax.install();
|
||||
});
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { AlfrescoApiServiceMock, AppConfigService, StorageService, ContentService, setupTestBed, CoreModule, TranslationMock } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiServiceMock, AppConfigService, StorageService, ContentService, setupTestBed, CoreModule, TranslationMock, UserPreferencesService } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { FileNode, FolderNode } from '../../mock';
|
||||
import { ContentActionHandler } from '../models/content-action.model';
|
||||
@@ -27,6 +27,7 @@ describe('FolderActionsService', () => {
|
||||
|
||||
let service: FolderActionsService;
|
||||
let documentListService: DocumentListService;
|
||||
let userPreferences: UserPreferencesService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
@@ -38,8 +39,10 @@ describe('FolderActionsService', () => {
|
||||
let appConfig: AppConfigService = TestBed.get(AppConfigService);
|
||||
appConfig.config.ecmHost = 'http://localhost:9876/ecm';
|
||||
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
|
||||
let contentService = new ContentService(null, null, null, null);
|
||||
let alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
|
||||
let alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), userPreferences, new StorageService());
|
||||
documentListService = new DocumentListService(null, contentService, alfrescoApiService, null, null);
|
||||
service = new FolderActionsService(null, documentListService, contentService, new TranslationMock());
|
||||
});
|
||||
|
@@ -15,17 +15,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AlfrescoApiServiceMock, AppConfigService, StorageService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiServiceMock, AppConfigService, StorageService, setupTestBed, CoreModule, UserPreferencesService } from '@alfresco/adf-core';
|
||||
import { RatingService } from './rating.service';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
describe('Rating service', () => {
|
||||
|
||||
let service;
|
||||
let userPreferences: UserPreferencesService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = new RatingService(new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService()));
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
|
||||
service = new RatingService(new AlfrescoApiServiceMock(new AppConfigService(null), userPreferences, new StorageService()));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@@ -15,17 +15,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AlfrescoApiServiceMock, LogService, AppConfigService, StorageService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiServiceMock, LogService, AppConfigService, StorageService, setupTestBed, CoreModule, UserPreferencesService } from '@alfresco/adf-core';
|
||||
import { TagService } from './tag.service';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
describe('TagService', () => {
|
||||
|
||||
let service: TagService;
|
||||
let userPreferences: UserPreferencesService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = new TagService(new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService()), new LogService(new AppConfigService(null)));
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
service = new TagService(new AlfrescoApiServiceMock(new AppConfigService(null), userPreferences, new StorageService()), new LogService(new AppConfigService(null)));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@@ -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;
|
||||
|
@@ -15,11 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async } from '@angular/core/testing';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { mockError, fakeProcessFilters } from '../../mock';
|
||||
import { FilterProcessRepresentationModel } from '../models/filter-process.model';
|
||||
import { ProcessFilterService } from './process-filter.service';
|
||||
import { AlfrescoApiServiceMock, AlfrescoApiService, AppConfigService, StorageService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiServiceMock, AlfrescoApiService, AppConfigService, StorageService, UserPreferencesService, setupTestBed, CoreModule } from '@alfresco/adf-core';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
@@ -28,9 +28,17 @@ describe('Process filter', () => {
|
||||
let service: ProcessFilterService;
|
||||
let apiService: AlfrescoApiService;
|
||||
let alfrescoApi: any;
|
||||
let userPreferences: UserPreferencesService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
apiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService() );
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
apiService = new AlfrescoApiServiceMock(new AppConfigService(null), userPreferences, new StorageService() );
|
||||
service = new ProcessFilterService(apiService);
|
||||
alfrescoApi = apiService.getInstance();
|
||||
});
|
||||
|
@@ -15,13 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async } from '@angular/core/testing';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { exampleProcess, fakeProcessInstances } from '../../mock';
|
||||
import { mockError, fakeProcessDef, fakeTasksList } from '../../mock';
|
||||
import { ProcessFilterParamRepresentationModel } from '../models/filter-process.model';
|
||||
import { ProcessInstanceVariable } from '../models/process-instance-variable.model';
|
||||
import { ProcessService } from './process.service';
|
||||
import { AlfrescoApiService, AlfrescoApiServiceMock, AppConfigService, StorageService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiService, AlfrescoApiServiceMock, AppConfigService, StorageService, UserPreferencesService, setupTestBed, CoreModule } from '@alfresco/adf-core';
|
||||
|
||||
declare let moment: any;
|
||||
|
||||
@@ -30,9 +30,17 @@ describe('ProcessService', () => {
|
||||
let service: ProcessService;
|
||||
let apiService: AlfrescoApiService;
|
||||
let alfrescoApi: any;
|
||||
let userPreferences: UserPreferencesService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
apiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService() );
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
apiService = new AlfrescoApiServiceMock(new AppConfigService(null), userPreferences, new StorageService() );
|
||||
service = new ProcessService(apiService);
|
||||
alfrescoApi = apiService.getInstance();
|
||||
});
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async } from '@angular/core/testing';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import {
|
||||
fakeAppFilter,
|
||||
fakeAppPromise,
|
||||
@@ -23,15 +23,24 @@ import {
|
||||
} from '../../mock';
|
||||
import { FilterRepresentationModel } from '../models/filter.model';
|
||||
import { TaskFilterService } from './task-filter.service';
|
||||
import { AlfrescoApiServiceMock, LogService, AppConfigService, StorageService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiServiceMock, LogService, AppConfigService, StorageService, setupTestBed, CoreModule, UserPreferencesService } from '@alfresco/adf-core';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
describe('Activiti Task filter Service', () => {
|
||||
|
||||
let service: TaskFilterService;
|
||||
let userPreferences: UserPreferencesService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
service = new TaskFilterService(new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService()), new LogService(new AppConfigService(null)));
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
service = new TaskFilterService(new AlfrescoApiServiceMock(new AppConfigService(null), userPreferences, new StorageService()), new LogService(new AppConfigService(null)));
|
||||
jasmine.Ajax.install();
|
||||
}));
|
||||
|
||||
|
@@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async } from '@angular/core/testing';
|
||||
import { UserProcessModel } from '@alfresco/adf-core';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { UserProcessModel, setupTestBed, CoreModule } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import {
|
||||
fakeCompletedTaskList,
|
||||
@@ -35,16 +35,24 @@ import {
|
||||
import { FilterRepresentationModel, TaskQueryRequestRepresentationModel } from '../models/filter.model';
|
||||
import { TaskDetailsModel } from '../models/task-details.model';
|
||||
import { TaskListService } from './tasklist.service';
|
||||
import { AlfrescoApiServiceMock, LogService, AppConfigService, StorageService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiServiceMock, LogService, AppConfigService, StorageService, UserPreferencesService } from '@alfresco/adf-core';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
describe('Activiti TaskList Service', () => {
|
||||
|
||||
let service: TaskListService;
|
||||
let userPreferences: UserPreferencesService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
service = new TaskListService(new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService() ), new LogService(new AppConfigService(null)));
|
||||
userPreferences = TestBed.get(UserPreferencesService);
|
||||
service = new TaskListService(new AlfrescoApiServiceMock(new AppConfigService(null), userPreferences, new StorageService() ), new LogService(new AppConfigService(null)));
|
||||
jasmine.Ajax.install();
|
||||
}));
|
||||
|
||||
|
133
package-lock.json
generated
133
package-lock.json
generated
@@ -1033,6 +1033,14 @@
|
||||
"tslib": "1.9.1"
|
||||
}
|
||||
},
|
||||
"@auth0/angular-jwt": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@auth0/angular-jwt/-/angular-jwt-1.2.0.tgz",
|
||||
"integrity": "sha512-T2t8ed4/g/w5aniVmOJ7AieTrQ9iU7urAbzRBx5OToY4UeEzzng+yP4yk114Xwoi3iV9AS4zhqMqgKcQ3vNvxA==",
|
||||
"requires": {
|
||||
"url": "0.11.0"
|
||||
}
|
||||
},
|
||||
"@mat-datetimepicker/core": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@mat-datetimepicker/core/-/core-1.0.4.tgz",
|
||||
@@ -1403,9 +1411,9 @@
|
||||
}
|
||||
},
|
||||
"alfresco-js-api": {
|
||||
"version": "2.4.0-beta6",
|
||||
"resolved": "https://registry.npmjs.org/alfresco-js-api/-/alfresco-js-api-2.4.0-beta6.tgz",
|
||||
"integrity": "sha512-ytEScVOdD9MeoIqkYwBHVE/GpmwD/0PE9eEEMZLzs/7r5igDhqIw4BrleIerfXh/S4y+U15fP0UFv++BW2XKEQ==",
|
||||
"version": "2.4.0-fed4e011ee70eb36b5e2015859e719153d70b6c2",
|
||||
"resolved": "https://registry.npmjs.org/alfresco-js-api/-/alfresco-js-api-2.4.0-fed4e011ee70eb36b5e2015859e719153d70b6c2.tgz",
|
||||
"integrity": "sha512-HWA2zLbuRTi1mjIoaEW2ZSWKMDfOWcYOgplFZovQgXUOaFZuPJH1AQ7RbImq/G5/NKwe9AJSUAnkg5vpuKEBTg==",
|
||||
"requires": {
|
||||
"event-emitter": "0.3.4",
|
||||
"superagent": "3.8.2"
|
||||
@@ -1477,6 +1485,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"angular-oauth2-oidc": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/angular-oauth2-oidc/-/angular-oauth2-oidc-3.1.4.tgz",
|
||||
"integrity": "sha1-B3k5VhbXLxXp3985Ld2pxpY3Qug=",
|
||||
"requires": {
|
||||
"jsrsasign": "8.0.12"
|
||||
}
|
||||
},
|
||||
"angular2-template-loader": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/angular2-template-loader/-/angular2-template-loader-0.6.2.tgz",
|
||||
@@ -5888,30 +5904,6 @@
|
||||
"locate-path": "2.0.0"
|
||||
}
|
||||
},
|
||||
"findup": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/findup/-/findup-0.1.5.tgz",
|
||||
"integrity": "sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colors": "0.6.2",
|
||||
"commander": "2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"colors": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
|
||||
"integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz",
|
||||
"integrity": "sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"flatten": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz",
|
||||
@@ -6853,28 +6845,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ghooks": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/ghooks/-/ghooks-2.0.4.tgz",
|
||||
"integrity": "sha1-/VDgQP9UiQauQstReToBv+JFZ7k=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"findup": "0.1.5",
|
||||
"lodash.clone": "4.5.0",
|
||||
"manage-path": "2.0.0",
|
||||
"opt-cli": "1.5.1",
|
||||
"path-exists": "3.0.0",
|
||||
"spawn-command": "0.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"spawn-command": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz",
|
||||
"integrity": "sha1-lUThpDygRfhTGqwaSMspva5iM44=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"github-build": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/github-build/-/github-build-1.2.0.tgz",
|
||||
@@ -8951,6 +8921,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsrsasign": {
|
||||
"version": "8.0.12",
|
||||
"resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz",
|
||||
"integrity": "sha1-Iqu5ZW00owuVMENnIINeicLlwxY="
|
||||
},
|
||||
"karma": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/karma/-/karma-2.0.2.tgz",
|
||||
@@ -9467,14 +9442,7 @@
|
||||
"lodash": {
|
||||
"version": "4.17.10",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash._baseclone": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-4.5.7.tgz",
|
||||
"integrity": "sha1-zkKt4IOE711i+nfDD2GkbmhvhDQ=",
|
||||
"dev": true
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
|
||||
},
|
||||
"lodash._basecopy": {
|
||||
"version": "3.0.1",
|
||||
@@ -9555,12 +9523,6 @@
|
||||
"lodash._createcompounder": "3.0.0"
|
||||
}
|
||||
},
|
||||
"lodash.clone": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
|
||||
"integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
@@ -10018,12 +9980,6 @@
|
||||
"integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==",
|
||||
"dev": true
|
||||
},
|
||||
"manage-path": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/manage-path/-/manage-path-2.0.0.tgz",
|
||||
"integrity": "sha1-9M+EV7km7u4qg7FzUBQUvHbrlZc=",
|
||||
"dev": true
|
||||
},
|
||||
"map-cache": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
|
||||
@@ -11519,38 +11475,6 @@
|
||||
"is-wsl": "1.1.0"
|
||||
}
|
||||
},
|
||||
"opt-cli": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/opt-cli/-/opt-cli-1.5.1.tgz",
|
||||
"integrity": "sha1-BNtEexPJa5kusxaFJm9O0NlzbcI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "2.9.0",
|
||||
"lodash.clone": "4.3.2",
|
||||
"manage-path": "2.0.0",
|
||||
"spawn-command": "0.0.2-1"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
|
||||
"integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-readlink": "1.0.1"
|
||||
}
|
||||
},
|
||||
"lodash.clone": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.3.2.tgz",
|
||||
"integrity": "sha1-5WsXa2gjp93jj38r9Y3n1ZcSAOk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._baseclone": "4.5.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
|
||||
@@ -14575,8 +14499,7 @@
|
||||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
|
||||
"dev": true
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
||||
},
|
||||
"querystring-es3": {
|
||||
"version": "0.2.1",
|
||||
@@ -18756,7 +18679,6 @@
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
|
||||
"integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"punycode": "1.3.2",
|
||||
"querystring": "0.2.0"
|
||||
@@ -18765,8 +18687,7 @@
|
||||
"punycode": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
|
||||
"dev": true
|
||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
Reference in New Issue
Block a user