Fix display order of authentication methods

This commit is contained in:
Alexandre Vryghem
2023-08-02 00:00:37 +02:00
parent ca864379c8
commit 71cf66ecf4
20 changed files with 107 additions and 173 deletions

View File

@@ -152,12 +152,12 @@ export class AuthInterceptor implements HttpInterceptor {
let authMethodModel: AuthMethod; let authMethodModel: AuthMethod;
if (splittedRealm.length === 1) { if (splittedRealm.length === 1) {
authMethodModel = new AuthMethod(methodName); authMethodModel = new AuthMethod(methodName, Number(j));
authMethodModels.push(authMethodModel); authMethodModels.push(authMethodModel);
} else if (splittedRealm.length > 1) { } else if (splittedRealm.length > 1) {
let location = splittedRealm[1]; let location = splittedRealm[1];
location = this.parseLocation(location); location = this.parseLocation(location);
authMethodModel = new AuthMethod(methodName, location); authMethodModel = new AuthMethod(methodName, Number(j), location);
authMethodModels.push(authMethodModel); authMethodModels.push(authMethodModel);
} }
} }
@@ -165,7 +165,7 @@ export class AuthInterceptor implements HttpInterceptor {
// make sure the email + password login component gets rendered first // make sure the email + password login component gets rendered first
authMethodModels = this.sortAuthMethods(authMethodModels); authMethodModels = this.sortAuthMethods(authMethodModels);
} else { } else {
authMethodModels.push(new AuthMethod(AuthMethodType.Password)); authMethodModels.push(new AuthMethod(AuthMethodType.Password, 0));
} }
return authMethodModels; return authMethodModels;

View File

@@ -575,9 +575,9 @@ describe('authReducer', () => {
authMethods: [], authMethods: [],
idle: false idle: false
}; };
const authMethods = [ const authMethods: AuthMethod[] = [
new AuthMethod(AuthMethodType.Password), new AuthMethod(AuthMethodType.Password, 0),
new AuthMethod(AuthMethodType.Shibboleth, 'location') new AuthMethod(AuthMethodType.Shibboleth, 1, 'location'),
]; ];
const action = new RetrieveAuthMethodsSuccessAction(authMethods); const action = new RetrieveAuthMethodsSuccessAction(authMethods);
const newState = authReducer(initialState, action); const newState = authReducer(initialState, action);
@@ -609,7 +609,7 @@ describe('authReducer', () => {
loaded: false, loaded: false,
blocking: false, blocking: false,
loading: false, loading: false,
authMethods: [new AuthMethod(AuthMethodType.Password)], authMethods: [new AuthMethod(AuthMethodType.Password, 0)],
idle: false idle: false
}; };
expect(newState).toEqual(state); expect(newState).toEqual(state);

View File

@@ -228,7 +228,7 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
return Object.assign({}, state, { return Object.assign({}, state, {
loading: false, loading: false,
blocking: false, blocking: false,
authMethods: [new AuthMethod(AuthMethodType.Password)] authMethods: [new AuthMethod(AuthMethodType.Password, 0)]
}); });
case AuthActionTypes.SET_REDIRECT_URL: case AuthActionTypes.SET_REDIRECT_URL:

View File

@@ -2,11 +2,12 @@ import { AuthMethodType } from './auth.method-type';
export class AuthMethod { export class AuthMethod {
authMethodType: AuthMethodType; authMethodType: AuthMethodType;
position: number;
location?: string; location?: string;
// isStandalonePage? = true; constructor(authMethodName: string, position: number, location?: string) {
this.position = position;
constructor(authMethodName: string, location?: string) {
switch (authMethodName) { switch (authMethodName) {
case 'ip': { case 'ip': {
this.authMethodType = AuthMethodType.Ip; this.authMethodType = AuthMethodType.Ip;

View File

@@ -13,13 +13,17 @@ import { AuthMethod } from '../../../core/auth/models/auth.method';
import { AuthServiceStub } from '../../testing/auth-service.stub'; import { AuthServiceStub } from '../../testing/auth-service.stub';
import { createTestComponent } from '../../testing/utils.test'; import { createTestComponent } from '../../testing/utils.test';
import { HardRedirectService } from '../../../core/services/hard-redirect.service'; import { HardRedirectService } from '../../../core/services/hard-redirect.service';
import { AuthMethodType } from '../../../core/auth/models/auth.method-type';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { AuthorizationDataServiceStub } from '../../testing/authorization-service.stub';
import { RouterTestingModule } from '@angular/router/testing';
describe('LogInContainerComponent', () => { describe('LogInContainerComponent', () => {
let component: LogInContainerComponent; let component: LogInContainerComponent;
let fixture: ComponentFixture<LogInContainerComponent>; let fixture: ComponentFixture<LogInContainerComponent>;
const authMethod = new AuthMethod('password'); const authMethod = new AuthMethod(AuthMethodType.Password, 0);
let hardRedirectService: HardRedirectService; let hardRedirectService: HardRedirectService;
@@ -35,13 +39,15 @@ describe('LogInContainerComponent', () => {
ReactiveFormsModule, ReactiveFormsModule,
StoreModule.forRoot(authReducer), StoreModule.forRoot(authReducer),
SharedModule, SharedModule,
TranslateModule.forRoot() TranslateModule.forRoot(),
RouterTestingModule,
], ],
declarations: [ declarations: [
TestComponent TestComponent
], ],
providers: [ providers: [
{ provide: AuthService, useClass: AuthServiceStub }, { provide: AuthService, useClass: AuthServiceStub },
{ provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub },
{ provide: HardRedirectService, useValue: hardRedirectService }, { provide: HardRedirectService, useValue: hardRedirectService },
LogInContainerComponent LogInContainerComponent
], ],
@@ -113,6 +119,6 @@ describe('LogInContainerComponent', () => {
class TestComponent { class TestComponent {
isStandalonePage = true; isStandalonePage = true;
authMethod = new AuthMethod('password'); authMethod = new AuthMethod(AuthMethodType.Password, 0);
} }

View File

@@ -1,13 +1,11 @@
<ds-themed-loading *ngIf="(loading | async) || (isAuthenticated | async)" class="m-5"></ds-themed-loading> <ds-themed-loading *ngIf="(loading | async) || (isAuthenticated | async)" class="m-5"></ds-themed-loading>
<div *ngIf="!(loading | async) && !(isAuthenticated | async)" class="px-4 py-3 login-container"> <div *ngIf="!(loading | async) && !(isAuthenticated | async)" class="px-4 py-3 login-container">
<ng-container *ngFor="let authMethod of (authMethods | async); let i = index"> <ng-container *ngFor="let authMethod of getOrderedAuthMethods(authMethods | async); let last = last">
<div *ngIf="i === 1" class="text-center mt-2"> <div [class.d-none]="contentRef.innerText.trim().length === 0">
<span class="align-middle">{{"login.form.or-divider" | translate}}</span> <div #contentRef>
</div>
<ds-log-in-container [authMethod]="authMethod" [isStandalonePage]="isStandalonePage"></ds-log-in-container> <ds-log-in-container [authMethod]="authMethod" [isStandalonePage]="isStandalonePage"></ds-log-in-container>
</div>
<div *ngIf="!last" class="dropdown-divider my-2"></div>
</div>
</ng-container> </ng-container>
<div class="dropdown-divider"></div>
<a class="dropdown-item" *ngIf="canRegister$ | async" [routerLink]="[getRegisterRoute()]" [attr.data-test]="'register' | dsBrowserOnly">{{"login.form.new-user" | translate}}</a>
<a class="dropdown-item" [routerLink]="[getForgotRoute()]" [attr.data-test]="'forgot' | dsBrowserOnly">{{"login.form.forgot-password" | translate}}</a>
</div> </div>

View File

@@ -50,7 +50,7 @@ describe('LogInComponent', () => {
}); });
// refine the test module by declaring the test component // refine the test module by declaring the test component
TestBed.configureTestingModule({ void TestBed.configureTestingModule({
imports: [ imports: [
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,

View File

@@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
import { AuthMethod } from '../../core/auth/models/auth.method'; import { AuthMethod } from '../../core/auth/models/auth.method';
@@ -8,11 +8,8 @@ import {
isAuthenticated, isAuthenticated,
isAuthenticationLoading isAuthenticationLoading
} from '../../core/auth/selectors'; } from '../../core/auth/selectors';
import { getForgotPasswordRoute, getRegisterRoute } from '../../app-routing-paths';
import { hasValue } from '../empty.util'; import { hasValue } from '../empty.util';
import { AuthService } from '../../core/auth/auth.service'; import { AuthService } from '../../core/auth/auth.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { CoreState } from '../../core/core-state.model'; import { CoreState } from '../../core/core-state.model';
/** /**
@@ -22,7 +19,8 @@ import { CoreState } from '../../core/core-state.model';
@Component({ @Component({
selector: 'ds-log-in', selector: 'ds-log-in',
templateUrl: './log-in.component.html', templateUrl: './log-in.component.html',
styleUrls: ['./log-in.component.scss'] styleUrls: ['./log-in.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class LogInComponent implements OnInit { export class LogInComponent implements OnInit {
@@ -50,14 +48,9 @@ export class LogInComponent implements OnInit {
*/ */
public loading: Observable<boolean>; public loading: Observable<boolean>;
/**
* Whether or not the current user (or anonymous) is authorized to register an account
*/
canRegister$: Observable<boolean>;
constructor(private store: Store<CoreState>, constructor(private store: Store<CoreState>,
private authService: AuthService, private authService: AuthService,
private authorizationService: AuthorizationDataService) { ) {
} }
ngOnInit(): void { ngOnInit(): void {
@@ -78,15 +71,18 @@ export class LogInComponent implements OnInit {
this.authService.clearRedirectUrl(); this.authService.clearRedirectUrl();
} }
}); });
this.canRegister$ = this.authorizationService.isAuthorized(FeatureID.EPersonRegistration);
} }
getRegisterRoute() { /**
return getRegisterRoute(); * Returns an ordered list of {@link AuthMethod}s based on their position.
*
* @param authMethods The {@link AuthMethod}s to sort
*/
getOrderedAuthMethods(authMethods: AuthMethod[] | null): AuthMethod[] {
if (hasValue(authMethods)) {
return [...authMethods].sort((method1: AuthMethod, method2: AuthMethod) => method1.position - method2.position);
} else {
return [];
} }
getForgotRoute() {
return getForgotPasswordRoute();
} }
} }

View File

@@ -1,3 +1,3 @@
<button class="btn btn-lg btn-primary btn-block mt-2 text-white" (click)="redirectToOidc()"> <button class="btn btn-lg btn-primary btn-block text-white" (click)="redirectToOidc()">
<i class="fas fa-sign-in-alt"></i> {{"login.form.oidc" | translate}} <i class="fas fa-sign-in-alt"></i> {{"login.form.oidc" | translate}}
</button> </button>

View File

@@ -3,11 +3,8 @@ import { ActivatedRoute, Router } from '@angular/router';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { Store, StoreModule } from '@ngrx/store'; import { StoreModule } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { EPerson } from '../../../../core/eperson/models/eperson.model';
import { EPersonMock } from '../../../testing/eperson.mock';
import { authReducer } from '../../../../core/auth/auth.reducer'; import { authReducer } from '../../../../core/auth/auth.reducer';
import { AuthService } from '../../../../core/auth/auth.service'; import { AuthService } from '../../../../core/auth/auth.service';
import { AuthServiceStub } from '../../../testing/auth-service.stub'; import { AuthServiceStub } from '../../../testing/auth-service.stub';
@@ -20,23 +17,22 @@ import { RouterStub } from '../../../testing/router.stub';
import { ActivatedRouteStub } from '../../../testing/active-router.stub'; import { ActivatedRouteStub } from '../../../testing/active-router.stub';
import { NativeWindowMockFactory } from '../../../mocks/mock-native-window-ref'; import { NativeWindowMockFactory } from '../../../mocks/mock-native-window-ref';
import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; import { HardRedirectService } from '../../../../core/services/hard-redirect.service';
import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service';
import { AuthorizationDataServiceStub } from '../../../testing/authorization-service.stub';
describe('LogInOidcComponent', () => { describe('LogInOidcComponent', () => {
let component: LogInOidcComponent; let component: LogInOidcComponent;
let fixture: ComponentFixture<LogInOidcComponent>; let fixture: ComponentFixture<LogInOidcComponent>;
let page: Page;
let user: EPerson;
let componentAsAny: any; let componentAsAny: any;
let setHrefSpy; let setHrefSpy;
let oidcBaseUrl; let oidcBaseUrl: string;
let location; let location: string;
let initialState: any; let initialState: any;
let hardRedirectService: HardRedirectService; let hardRedirectService: HardRedirectService;
beforeEach(() => { beforeEach(() => {
user = EPersonMock;
oidcBaseUrl = 'dspace-rest.test/oidc?redirectUrl='; oidcBaseUrl = 'dspace-rest.test/oidc?redirectUrl=';
location = oidcBaseUrl + 'http://dspace-angular.test/home'; location = oidcBaseUrl + 'http://dspace-angular.test/home';
@@ -60,7 +56,7 @@ describe('LogInOidcComponent', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
// refine the test module by declaring the test component // refine the test module by declaring the test component
TestBed.configureTestingModule({ void TestBed.configureTestingModule({
imports: [ imports: [
StoreModule.forRoot({ auth: authReducer }, storeModuleConfig), StoreModule.forRoot({ auth: authReducer }, storeModuleConfig),
TranslateModule.forRoot() TranslateModule.forRoot()
@@ -70,7 +66,8 @@ describe('LogInOidcComponent', () => {
], ],
providers: [ providers: [
{ provide: AuthService, useClass: AuthServiceStub }, { provide: AuthService, useClass: AuthServiceStub },
{ provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Oidc, location) }, { provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub },
{ provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Oidc, 0, location) },
{ provide: 'isStandalonePage', useValue: true }, { provide: 'isStandalonePage', useValue: true },
{ provide: NativeWindowService, useFactory: NativeWindowMockFactory }, { provide: NativeWindowService, useFactory: NativeWindowMockFactory },
{ provide: Router, useValue: new RouterStub() }, { provide: Router, useValue: new RouterStub() },
@@ -95,7 +92,6 @@ describe('LogInOidcComponent', () => {
componentAsAny = component; componentAsAny = component;
// create page // create page
page = new Page(component, fixture);
setHrefSpy = spyOnProperty(componentAsAny._window.nativeWindow.location, 'href', 'set').and.callThrough(); setHrefSpy = spyOnProperty(componentAsAny._window.nativeWindow.location, 'href', 'set').and.callThrough();
}); });
@@ -131,25 +127,3 @@ describe('LogInOidcComponent', () => {
}); });
}); });
/**
* I represent the DOM elements and attach spies.
*
* @class Page
*/
class Page {
public emailInput: HTMLInputElement;
public navigateSpy: jasmine.Spy;
public passwordInput: HTMLInputElement;
constructor(private component: LogInOidcComponent, private fixture: ComponentFixture<LogInOidcComponent>) {
// use injector to get services
const injector = fixture.debugElement.injector;
const store = injector.get(Store);
// add spies
this.navigateSpy = spyOn(store, 'dispatch');
}
}

View File

@@ -1,3 +1,3 @@
<button class="btn btn-lg btn-primary btn-block mt-2 text-white" (click)="redirectToOrcid()"> <button class="btn btn-lg btn-primary btn-block text-white" (click)="redirectToOrcid()">
<i class="fas fa-sign-in-alt"></i> {{"login.form.orcid" | translate}} <i class="fas fa-sign-in-alt"></i> {{"login.form.orcid" | translate}}
</button> </button>

View File

@@ -3,11 +3,8 @@ import { ActivatedRoute, Router } from '@angular/router';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { Store, StoreModule } from '@ngrx/store'; import { StoreModule } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { EPerson } from '../../../../core/eperson/models/eperson.model';
import { EPersonMock } from '../../../testing/eperson.mock';
import { authReducer } from '../../../../core/auth/auth.reducer'; import { authReducer } from '../../../../core/auth/auth.reducer';
import { AuthService } from '../../../../core/auth/auth.service'; import { AuthService } from '../../../../core/auth/auth.service';
import { AuthServiceStub } from '../../../testing/auth-service.stub'; import { AuthServiceStub } from '../../../testing/auth-service.stub';
@@ -26,17 +23,14 @@ describe('LogInOrcidComponent', () => {
let component: LogInOrcidComponent; let component: LogInOrcidComponent;
let fixture: ComponentFixture<LogInOrcidComponent>; let fixture: ComponentFixture<LogInOrcidComponent>;
let page: Page;
let user: EPerson;
let componentAsAny: any; let componentAsAny: any;
let setHrefSpy; let setHrefSpy;
let orcidBaseUrl; let orcidBaseUrl: string;
let location; let location: string;
let initialState: any; let initialState: any;
let hardRedirectService: HardRedirectService; let hardRedirectService: HardRedirectService;
beforeEach(() => { beforeEach(() => {
user = EPersonMock;
orcidBaseUrl = 'dspace-rest.test/orcid?redirectUrl='; orcidBaseUrl = 'dspace-rest.test/orcid?redirectUrl=';
location = orcidBaseUrl + 'http://dspace-angular.test/home'; location = orcidBaseUrl + 'http://dspace-angular.test/home';
@@ -60,7 +54,7 @@ describe('LogInOrcidComponent', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
// refine the test module by declaring the test component // refine the test module by declaring the test component
TestBed.configureTestingModule({ void TestBed.configureTestingModule({
imports: [ imports: [
StoreModule.forRoot({ auth: authReducer }, storeModuleConfig), StoreModule.forRoot({ auth: authReducer }, storeModuleConfig),
TranslateModule.forRoot() TranslateModule.forRoot()
@@ -70,7 +64,7 @@ describe('LogInOrcidComponent', () => {
], ],
providers: [ providers: [
{ provide: AuthService, useClass: AuthServiceStub }, { provide: AuthService, useClass: AuthServiceStub },
{ provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Orcid, location) }, { provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Orcid, 0, location) },
{ provide: 'isStandalonePage', useValue: true }, { provide: 'isStandalonePage', useValue: true },
{ provide: NativeWindowService, useFactory: NativeWindowMockFactory }, { provide: NativeWindowService, useFactory: NativeWindowMockFactory },
{ provide: Router, useValue: new RouterStub() }, { provide: Router, useValue: new RouterStub() },
@@ -95,7 +89,6 @@ describe('LogInOrcidComponent', () => {
componentAsAny = component; componentAsAny = component;
// create page // create page
page = new Page(component, fixture);
setHrefSpy = spyOnProperty(componentAsAny._window.nativeWindow.location, 'href', 'set').and.callThrough(); setHrefSpy = spyOnProperty(componentAsAny._window.nativeWindow.location, 'href', 'set').and.callThrough();
}); });
@@ -131,25 +124,3 @@ describe('LogInOrcidComponent', () => {
}); });
}); });
/**
* I represent the DOM elements and attach spies.
*
* @class Page
*/
class Page {
public emailInput: HTMLInputElement;
public navigateSpy: jasmine.Spy;
public passwordInput: HTMLInputElement;
constructor(private component: LogInOrcidComponent, private fixture: ComponentFixture<LogInOrcidComponent>) {
// use injector to get services
const injector = fixture.debugElement.injector;
const store = injector.get(Store);
// add spies
this.navigateSpy = spyOn(store, 'dispatch');
}
}

View File

@@ -28,3 +28,12 @@
<button class="btn btn-lg btn-primary btn-block mt-3" type="submit" [attr.data-test]="'login-button' | dsBrowserOnly" <button class="btn btn-lg btn-primary btn-block mt-3" type="submit" [attr.data-test]="'login-button' | dsBrowserOnly"
[disabled]="!form.valid"><i class="fas fa-sign-in-alt"></i> {{"login.form.submit" | translate}}</button> [disabled]="!form.valid"><i class="fas fa-sign-in-alt"></i> {{"login.form.submit" | translate}}</button>
</form> </form>
<div class="mt-2">
<a class="dropdown-item" *ngIf="canRegister$ | async" [routerLink]="[getRegisterRoute()]" [attr.data-test]="'register' | dsBrowserOnly">
{{ 'login.form.new-user' | translate }}
</a>
<a class="dropdown-item" [routerLink]="[getForgotRoute()]" [attr.data-test]="'forgot' | dsBrowserOnly">
{{ 'login.form.forgot-password' | translate }}
</a>
</div>

View File

@@ -11,3 +11,7 @@
border-top-right-radius: 0; border-top-right-radius: 0;
} }
.dropdown-item {
white-space: normal;
padding: .25rem .75rem;
}

View File

@@ -8,8 +8,6 @@ import { Store, StoreModule } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { LogInPasswordComponent } from './log-in-password.component'; import { LogInPasswordComponent } from './log-in-password.component';
import { EPerson } from '../../../../core/eperson/models/eperson.model';
import { EPersonMock } from '../../../testing/eperson.mock';
import { authReducer } from '../../../../core/auth/auth.reducer'; import { authReducer } from '../../../../core/auth/auth.reducer';
import { AuthService } from '../../../../core/auth/auth.service'; import { AuthService } from '../../../../core/auth/auth.service';
import { AuthServiceStub } from '../../../testing/auth-service.stub'; import { AuthServiceStub } from '../../../testing/auth-service.stub';
@@ -18,19 +16,18 @@ import { AuthMethod } from '../../../../core/auth/models/auth.method';
import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; import { AuthMethodType } from '../../../../core/auth/models/auth.method-type';
import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; import { HardRedirectService } from '../../../../core/services/hard-redirect.service';
import { BrowserOnlyMockPipe } from '../../../testing/browser-only-mock.pipe'; import { BrowserOnlyMockPipe } from '../../../testing/browser-only-mock.pipe';
import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service';
import { AuthorizationDataServiceStub } from '../../../testing/authorization-service.stub';
describe('LogInPasswordComponent', () => { describe('LogInPasswordComponent', () => {
let component: LogInPasswordComponent; let component: LogInPasswordComponent;
let fixture: ComponentFixture<LogInPasswordComponent>; let fixture: ComponentFixture<LogInPasswordComponent>;
let page: Page; let page: Page;
let user: EPerson;
let initialState: any; let initialState: any;
let hardRedirectService: HardRedirectService; let hardRedirectService: HardRedirectService;
beforeEach(() => { beforeEach(() => {
user = EPersonMock;
hardRedirectService = jasmine.createSpyObj('hardRedirectService', { hardRedirectService = jasmine.createSpyObj('hardRedirectService', {
getCurrentRoute: {} getCurrentRoute: {}
}); });
@@ -50,7 +47,7 @@ describe('LogInPasswordComponent', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
// refine the test module by declaring the test component // refine the test module by declaring the test component
TestBed.configureTestingModule({ void TestBed.configureTestingModule({
imports: [ imports: [
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
@@ -63,7 +60,8 @@ describe('LogInPasswordComponent', () => {
], ],
providers: [ providers: [
{ provide: AuthService, useClass: AuthServiceStub }, { provide: AuthService, useClass: AuthServiceStub },
{ provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Password) }, { provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub },
{ provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Password, 0) },
{ provide: 'isStandalonePage', useValue: true }, { provide: 'isStandalonePage', useValue: true },
{ provide: HardRedirectService, useValue: hardRedirectService }, { provide: HardRedirectService, useValue: hardRedirectService },
provideMockStore({ initialState }), provideMockStore({ initialState }),
@@ -76,7 +74,7 @@ describe('LogInPasswordComponent', () => {
})); }));
beforeEach(() => { beforeEach(async () => {
// create component and test fixture // create component and test fixture
fixture = TestBed.createComponent(LogInPasswordComponent); fixture = TestBed.createComponent(LogInPasswordComponent);
@@ -87,12 +85,10 @@ describe('LogInPasswordComponent', () => {
page = new Page(component, fixture); page = new Page(component, fixture);
// verify the fixture is stable (no pending tasks) // verify the fixture is stable (no pending tasks)
fixture.whenStable().then(() => { await fixture.whenStable();
page.addPageElements(); page.addPageElements();
}); });
});
it('should create a FormGroup comprised of FormControls', () => { it('should create a FormGroup comprised of FormControls', () => {
fixture.detectChanges(); fixture.detectChanges();
expect(component.form instanceof FormGroup).toBe(true); expect(component.form instanceof FormGroup).toBe(true);

View File

@@ -15,6 +15,9 @@ import { AuthMethod } from '../../../../core/auth/models/auth.method';
import { AuthService } from '../../../../core/auth/auth.service'; import { AuthService } from '../../../../core/auth/auth.service';
import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; import { HardRedirectService } from '../../../../core/services/hard-redirect.service';
import { CoreState } from '../../../../core/core-state.model'; import { CoreState } from '../../../../core/core-state.model';
import { getForgotPasswordRoute, getRegisterRoute } from '../../../../app-routing-paths';
import { FeatureID } from '../../../../core/data/feature-authorization/feature-id';
import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service';
/** /**
* /users/sign-in * /users/sign-in
@@ -66,21 +69,18 @@ export class LogInPasswordComponent implements OnInit {
public form: FormGroup; public form: FormGroup;
/** /**
* @constructor * Whether the current user (or anonymous) is authorized to register an account
* @param {AuthMethod} injectedAuthMethodModel
* @param {boolean} isStandalonePage
* @param {AuthService} authService
* @param {HardRedirectService} hardRedirectService
* @param {FormBuilder} formBuilder
* @param {Store<State>} store
*/ */
public canRegister$: Observable<boolean>;
constructor( constructor(
@Inject('authMethodProvider') public injectedAuthMethodModel: AuthMethod, @Inject('authMethodProvider') public injectedAuthMethodModel: AuthMethod,
@Inject('isStandalonePage') public isStandalonePage: boolean, @Inject('isStandalonePage') public isStandalonePage: boolean,
private authService: AuthService, private authService: AuthService,
private hardRedirectService: HardRedirectService, private hardRedirectService: HardRedirectService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private store: Store<CoreState> protected store: Store<CoreState>,
protected authorizationService: AuthorizationDataService,
) { ) {
this.authMethod = injectedAuthMethodModel; this.authMethod = injectedAuthMethodModel;
} }
@@ -115,6 +115,15 @@ export class LogInPasswordComponent implements OnInit {
}) })
); );
this.canRegister$ = this.authorizationService.isAuthorized(FeatureID.EPersonRegistration);
}
getRegisterRoute() {
return getRegisterRoute();
}
getForgotRoute() {
return getForgotPasswordRoute();
} }
/** /**

View File

@@ -1,3 +1,3 @@
<button class="btn btn-lg btn-primary btn-block mt-2 text-white" (click)="redirectToShibboleth()"> <button class="btn btn-lg btn-primary btn-block text-white" (click)="redirectToShibboleth()">
<i class="fas fa-sign-in-alt"></i> {{"login.form.shibboleth" | translate}} <i class="fas fa-sign-in-alt"></i> {{"login.form.shibboleth" | translate}}
</button> </button>

View File

@@ -3,11 +3,8 @@ import { ActivatedRoute, Router } from '@angular/router';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { Store, StoreModule } from '@ngrx/store'; import { StoreModule } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { EPerson } from '../../../../core/eperson/models/eperson.model';
import { EPersonMock } from '../../../testing/eperson.mock';
import { authReducer } from '../../../../core/auth/auth.reducer'; import { authReducer } from '../../../../core/auth/auth.reducer';
import { AuthService } from '../../../../core/auth/auth.service'; import { AuthService } from '../../../../core/auth/auth.service';
import { AuthServiceStub } from '../../../testing/auth-service.stub'; import { AuthServiceStub } from '../../../testing/auth-service.stub';
@@ -26,17 +23,14 @@ describe('LogInShibbolethComponent', () => {
let component: LogInShibbolethComponent; let component: LogInShibbolethComponent;
let fixture: ComponentFixture<LogInShibbolethComponent>; let fixture: ComponentFixture<LogInShibbolethComponent>;
let page: Page;
let user: EPerson;
let componentAsAny: any; let componentAsAny: any;
let setHrefSpy; let setHrefSpy;
let shibbolethBaseUrl; let shibbolethBaseUrl: string;
let location; let location: string;
let initialState: any; let initialState: any;
let hardRedirectService: HardRedirectService; let hardRedirectService: HardRedirectService;
beforeEach(() => { beforeEach(() => {
user = EPersonMock;
shibbolethBaseUrl = 'dspace-rest.test/shibboleth?redirectUrl='; shibbolethBaseUrl = 'dspace-rest.test/shibboleth?redirectUrl=';
location = shibbolethBaseUrl + 'http://dspace-angular.test/home'; location = shibbolethBaseUrl + 'http://dspace-angular.test/home';
@@ -60,7 +54,7 @@ describe('LogInShibbolethComponent', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
// refine the test module by declaring the test component // refine the test module by declaring the test component
TestBed.configureTestingModule({ void TestBed.configureTestingModule({
imports: [ imports: [
StoreModule.forRoot({ auth: authReducer }, storeModuleConfig), StoreModule.forRoot({ auth: authReducer }, storeModuleConfig),
TranslateModule.forRoot() TranslateModule.forRoot()
@@ -70,7 +64,7 @@ describe('LogInShibbolethComponent', () => {
], ],
providers: [ providers: [
{ provide: AuthService, useClass: AuthServiceStub }, { provide: AuthService, useClass: AuthServiceStub },
{ provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Shibboleth, location) }, { provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Shibboleth, 0, location) },
{ provide: 'isStandalonePage', useValue: true }, { provide: 'isStandalonePage', useValue: true },
{ provide: NativeWindowService, useFactory: NativeWindowMockFactory }, { provide: NativeWindowService, useFactory: NativeWindowMockFactory },
{ provide: Router, useValue: new RouterStub() }, { provide: Router, useValue: new RouterStub() },
@@ -95,7 +89,6 @@ describe('LogInShibbolethComponent', () => {
componentAsAny = component; componentAsAny = component;
// create page // create page
page = new Page(component, fixture);
setHrefSpy = spyOnProperty(componentAsAny._window.nativeWindow.location, 'href', 'set').and.callThrough(); setHrefSpy = spyOnProperty(componentAsAny._window.nativeWindow.location, 'href', 'set').and.callThrough();
}); });
@@ -131,25 +124,3 @@ describe('LogInShibbolethComponent', () => {
}); });
}); });
/**
* I represent the DOM elements and attach spies.
*
* @class Page
*/
class Page {
public emailInput: HTMLInputElement;
public navigateSpy: jasmine.Spy;
public passwordInput: HTMLInputElement;
constructor(private component: LogInShibbolethComponent, private fixture: ComponentFixture<LogInShibbolethComponent>) {
// use injector to get services
const injector = fixture.debugElement.injector;
const store = injector.get(Store);
// add spies
this.navigateSpy = spyOn(store, 'dispatch');
}
}

View File

@@ -6,10 +6,11 @@ import { EPerson } from '../../core/eperson/models/eperson.model';
import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
import { AuthMethod } from '../../core/auth/models/auth.method'; import { AuthMethod } from '../../core/auth/models/auth.method';
import { hasValue } from '../empty.util'; import { hasValue } from '../empty.util';
import { AuthMethodType } from '../../core/auth/models/auth.method-type';
export const authMethodsMock = [ export const authMethodsMock: AuthMethod[] = [
new AuthMethod('password'), new AuthMethod(AuthMethodType.Password, 0),
new AuthMethod('shibboleth', 'dspace.test/shibboleth') new AuthMethod(AuthMethodType.Shibboleth, 1, 'dspace.test/shibboleth'),
]; ];
export class AuthServiceStub { export class AuthServiceStub {

View File

@@ -2560,8 +2560,6 @@
"login.form.new-user": "New user? Click here to register.", "login.form.new-user": "New user? Click here to register.",
"login.form.or-divider": "or",
"login.form.oidc": "Log in with OIDC", "login.form.oidc": "Log in with OIDC",
"login.form.orcid": "Log in with ORCID", "login.form.orcid": "Log in with ORCID",