Skip to content

Commit d6776a8

Browse files
[CST-15074][DSpace#3355] PR review
1 parent f2c5912 commit d6776a8

9 files changed

Lines changed: 152 additions & 114 deletions

File tree

src/app/core/auth/auth.service.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from '@ngrx/store';
1212
import { TranslateService } from '@ngx-translate/core';
1313
import { CookieAttributes } from 'js-cookie';
14+
import uniqBy from 'lodash/uniqBy';
1415
import {
1516
Observable,
1617
of as observableOf,
@@ -38,6 +39,7 @@ import {
3839
isNotNull,
3940
isNotUndefined,
4041
} from '../../shared/empty.util';
42+
import { rendersAuthMethodType } from '../../shared/log-in/methods/log-in.methods-decorator';
4143
import { NotificationsService } from '../../shared/notifications/notifications.service';
4244
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
4345
import { followLink } from '../../shared/utils/follow-link-config.model';
@@ -74,13 +76,15 @@ import {
7476
} from './auth.actions';
7577
import { AuthRequestService } from './auth-request.service';
7678
import { AuthMethod } from './models/auth.method';
79+
import { AuthMethodType } from './models/auth.method-type';
7780
import { AuthStatus } from './models/auth-status.model';
7881
import {
7982
AuthTokenInfo,
8083
TOKENITEM,
8184
} from './models/auth-token-info.model';
8285
import {
8386
getAuthenticatedUserId,
87+
getAuthenticationMethods,
8488
getAuthenticationToken,
8589
getExternalAuthCookieStatus,
8690
getRedirectUrl,
@@ -690,4 +694,18 @@ export class AuthService {
690694
}
691695
}
692696

697+
public getAuthMethods(excludedAuthMethod?: AuthMethodType): Observable<AuthMethod[]> {
698+
return this.store.pipe(
699+
select(getAuthenticationMethods),
700+
map((methods: AuthMethod[]) => methods
701+
// ignore the given auth method if it should be excluded
702+
.filter((authMethod: AuthMethod) => excludedAuthMethod == null || authMethod.authMethodType !== excludedAuthMethod)
703+
.filter((authMethod: AuthMethod) => rendersAuthMethodType(authMethod.authMethodType) !== undefined)
704+
.sort((method1: AuthMethod, method2: AuthMethod) => method1.position - method2.position),
705+
),
706+
// ignore the ip authentication method when it's returned by the backend
707+
map((authMethods: AuthMethod[]) => uniqBy(authMethods.filter(a => a.authMethodType !== AuthMethodType.Ip), 'authMethodType')),
708+
);
709+
}
710+
693711
}
Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,37 @@
1-
<h4 class="mb-3">
1+
<h4>
22
{{ "external-login.confirm-email.header" | translate }}
33
</h4>
44

55
<form [formGroup]="emailForm" (ngSubmit)="submitForm()">
66
<div class="form-group">
7-
<input
8-
type="email"
9-
id="email"
10-
formControlName="email"
11-
placeholder="profile.email@example.com"
12-
class="form-control form-control-lg position-relative"
13-
[attr.aria-label]="'external-login.confirmation.email-label' | translate"
14-
/>
15-
@if (emailForm.get('email').hasError('required') && emailForm.get('email').touched) {
16-
<div class="text-danger">
17-
{{ "external-login.confirmation.email-required" | translate }}
7+
<div class="form-row">
8+
<div class="col-12 my-2">
9+
<input
10+
type="email"
11+
id="email"
12+
formControlName="email"
13+
placeholder="profile.email@example.com"
14+
class="form-control form-control-lg position-relative"
15+
[attr.aria-label]="'external-login.confirmation.email-label' | translate"
16+
/>
17+
@if (emailForm.get('email').hasError('required') && emailForm.get('email').touched) {
18+
<div class="text-danger">
19+
{{ "external-login.confirmation.email-required" | translate }}
20+
</div>
21+
}
22+
@if (emailForm.get('email').hasError('email') && emailForm.get('email').touched) {
23+
<div class="text-danger">
24+
{{ "external-login.confirmation.email-invalid" | translate }}
25+
</div>
26+
}
1827
</div>
19-
}
20-
@if (emailForm.get('email').hasError('email') && emailForm.get('email').touched) {
21-
<div class="text-danger">
22-
{{ "external-login.confirmation.email-invalid" | translate }}
28+
</div>
29+
<div class="form-row">
30+
<div class="col-12">
31+
<button type="submit" class="btn btn-lg btn-primary w-100">
32+
{{ "external-login.confirm.button.label" | translate }}
33+
</button>
2334
</div>
24-
}
35+
</div>
2536
</div>
26-
<button type="submit" class="btn btn-lg btn-primary btn-block">
27-
{{ "external-login.confirm.button.label" | translate }}
28-
</button>
2937
</form>
Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,37 @@
1-
<h4 class="mb-3">
1+
<h4>
22
{{ "external-login.provide-email.header" | translate }}
33
</h4>
44

55
<form [formGroup]="emailForm" (ngSubmit)="submitForm()">
66
<div class="form-group">
7-
<input
8-
type="email"
9-
id="email"
10-
formControlName="email"
11-
class="form-control form-control-lg position-relative"
12-
[attr.aria-label]="'external-login.confirmation.email' | translate"
13-
/>
7+
<div class="form-row">
8+
<div class="col-12 my-2">
9+
<input
10+
type="email"
11+
id="email"
12+
formControlName="email"
13+
class="form-control form-control-lg position-relative"
14+
[attr.aria-label]="'external-login.confirmation.email' | translate"
15+
/>
1416

15-
@if (emailForm.get('email').hasError('required') && emailForm.get('email').touched) {
16-
<div class="text-danger">
17-
{{ "external-login.confirmation.email-required" | translate }}
17+
@if (emailForm.get('email').hasError('required') && emailForm.get('email').touched) {
18+
<div class="text-danger">
19+
{{ "external-login.confirmation.email-required" | translate }}
20+
</div>
21+
}
22+
@if (emailForm.get('email').hasError('email') && emailForm.get('email').touched) {
23+
<div class="text-danger">
24+
{{ "external-login.confirmation.email-invalid" | translate }}
25+
</div>
26+
}
1827
</div>
19-
}
20-
@if (emailForm.get('email').hasError('email') && emailForm.get('email').touched) {
21-
<div class="text-danger">
22-
{{ "external-login.confirmation.email-invalid" | translate }}
28+
</div>
29+
<div class="form-row">
30+
<div class="col-12">
31+
<button type="submit" class="btn btn-lg btn-primary w-100">
32+
{{ "external-login.provide-email.button.label" | translate }}
33+
</button>
2334
</div>
24-
}
35+
</div>
2536
</div>
26-
27-
<button type="submit" class="btn btn-lg btn-primary btn-block">
28-
{{ "external-login.provide-email.button.label" | translate }}
29-
</button>
3037
</form>
Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,38 @@
11
<div class="row">
2-
<h4>{{ 'external-login.confirmation.header' | translate}}</h4>
2+
<h4>{{ 'external-login.confirmation.header' | translate }}</h4>
33
</div>
44
<div class="row justify-content-center">
55
<ng-container *ngComponentOutlet="getExternalLoginConfirmationType(); injector: objectInjector;">
66
</ng-container>
77
</div>
8-
<ds-alert class="row mt-2" [type]="AlertTypeEnum.Info" [attr.data-test]="'info-text'">
8+
<ds-alert class="container mt-2" [type]="AlertTypeEnum.Info" [attr.data-test]="'info-text'">
99
{{ informationText }}
1010
</ds-alert>
11-
<div class="row justify-content-center">
12-
<div class="col-4 d-flex justify-content-end align-items-center">
13-
@if (registrationData.email) {
14-
<ds-confirm-email [registrationData]="registrationData" [token]="token"></ds-confirm-email>
15-
} @else {
11+
<div class="row d-flex justify-content-center">
12+
<div class="col-6 d-flex">
13+
<div class="col d-flex justify-content-center align-items-center">
14+
@if (registrationData.email) {
15+
<ds-confirm-email [registrationData]="registrationData" [token]="token"></ds-confirm-email>
16+
} @else {
1617
<ds-provide-email [registrationId]="registrationData.id" [token]="token"></ds-provide-email>
1718
}
18-
</div>
19-
<div class="col-1 align-items-center d-flex justify-content-center">
20-
<h4 class="mt-2">{{ 'external-login.component.or' | translate }}</h4>
21-
</div>
22-
<div class="col-4 align-items-center d-flex justify-content-start">
23-
<button class="btn block btn-lg btn-primary" (click)="openLoginModal(loginModal)">
24-
{{'external-login.connect-to-existing-account.label' | translate}}
25-
</button>
19+
</div>
20+
@if (hasAuthMethodTypes | async) {
21+
<div class="col-1 d-flex justify-content-center align-items-center">
22+
<h4 class="mt-2">{{ 'external-login.component.or' | translate }}</h4>
23+
</div>
24+
<div class="col d-flex justify-content-center align-items-center">
25+
<button class="btn block btn-lg btn-primary" (click)="openLoginModal(loginModal)">
26+
{{ 'external-login.connect-to-existing-account.label' | translate }}
27+
</button>
28+
</div>
29+
}
2630
</div>
2731
</div>
2832

2933
<ng-template #loginModal let-c="close" let-d="dismiss">
3034
<div class="modal-header">
31-
<h4 class="modal-title text-info"> {{'external-login.connect-to-existing-account.label' | translate}}</h4>
35+
<h4 class="modal-title text-info"> {{ 'external-login.connect-to-existing-account.label' | translate }}</h4>
3236
</div>
3337
<div class="modal-body">
3438
<div class="row justify-content-center">
@@ -42,7 +46,7 @@ <h4 class="modal-title text-info"> {{'external-login.connect-to-existing-account
4246
<div class="modal-footer">
4347
<button type="button" class="btn btn-outline-primary btn-sm" (click)="c('Close click');clearRedirectUrl()">
4448
<i class="fa fa-times" aria-hidden="true"></i>
45-
{{'external-login.modal.label.close' | translate}}
49+
{{ 'external-login.modal.label.close' | translate }}
4650
</button>
4751
</div>
4852
</ng-template>

src/app/external-log-in/external-log-in/external-log-in.component.ts

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { NgComponentOutlet } from '@angular/common';
1+
import {
2+
AsyncPipe,
3+
NgComponentOutlet,
4+
} from '@angular/common';
25
import {
36
ChangeDetectionStrategy,
47
Component,
@@ -15,6 +18,8 @@ import {
1518
TranslateModule,
1619
TranslateService,
1720
} from '@ngx-translate/core';
21+
import { Observable } from 'rxjs';
22+
import { map } from 'rxjs/operators';
1823

1924
import { AuthService } from '../../core/auth/auth.service';
2025
import { AuthMethodType } from '../../core/auth/models/auth.method-type';
@@ -46,13 +51,14 @@ import { ProvideEmailComponent } from '../email-confirmation/provide-email/provi
4651
ConfirmEmailComponent,
4752
ThemedLogInComponent,
4853
NgComponentOutlet,
54+
AsyncPipe,
4955
],
5056
standalone: true,
5157
})
5258
/**
5359
* This component is responsible to handle the external-login depending on the RegistrationData details provided
5460
*/
55-
export class ExternalLogInComponent implements OnInit, OnDestroy {
61+
export class ExternalLogInComponent implements OnInit, OnDestroy {
5662
/**
5763
* The AlertType enumeration for access in the component's template
5864
* @type {AlertType}
@@ -93,13 +99,18 @@ export class ExternalLogInComponent implements OnInit, OnDestroy {
9399
* Authentication method related to registration type
94100
*/
95101
relatedAuthMethod: AuthMethodType;
102+
/**
103+
* The observable to check if any auth method type is configured
104+
*/
105+
hasAuthMethodTypes: Observable<boolean>;
96106

97107
constructor(
98108
private injector: Injector,
99109
private translate: TranslateService,
100110
private modalService: NgbModal,
101111
private authService: AuthService,
102-
) { }
112+
) {
113+
}
103114

104115
/**
105116
* Provide the registration data object to the objectInjector.
@@ -121,33 +132,7 @@ export class ExternalLogInComponent implements OnInit, OnDestroy {
121132
this.informationText = hasValue(this.registrationData?.email)
122133
? this.generateInformationTextWhenEmail(this.registrationType)
123134
: this.generateInformationTextWhenNOEmail(this.registrationType);
124-
}
125-
126-
/**
127-
* Generate the information text to be displayed when the user has no email
128-
* @param authMethod the registration type
129-
*/
130-
private generateInformationTextWhenNOEmail(authMethod: string): string {
131-
if (authMethod) {
132-
const authMethodUppercase = authMethod.toUpperCase();
133-
return this.translate.instant('external-login.noEmail.informationText', {
134-
authMethod: authMethodUppercase,
135-
});
136-
}
137-
}
138-
139-
/**
140-
* Generate the information text to be displayed when the user has an email
141-
* @param authMethod the registration type
142-
*/
143-
private generateInformationTextWhenEmail(authMethod: string): string {
144-
if (authMethod) {
145-
const authMethodUppercase = authMethod.toUpperCase();
146-
return this.translate.instant(
147-
'external-login.haveEmail.informationText',
148-
{ authMethod: authMethodUppercase },
149-
);
150-
}
135+
this.hasAuthMethodTypes = this.authService.getAuthMethods(this.relatedAuthMethod).pipe(map(methods => methods.length > 0));
151136
}
152137

153138
/**
@@ -163,11 +148,8 @@ export class ExternalLogInComponent implements OnInit, OnDestroy {
163148
* @param content - The content to be displayed in the modal.
164149
*/
165150
openLoginModal(content: any) {
166-
setTimeout(() => {
167-
this.authService.setRedirectUrl(`/review-account/${this.token}`);
168-
}, 100);
169151
this.modalRef = this.modalService.open(content);
170-
152+
this.authService.setRedirectUrl(`/review-account/${this.token}`);
171153
this.modalRef.dismissed.subscribe(() => {
172154
this.clearRedirectUrl();
173155
});
@@ -183,4 +165,31 @@ export class ExternalLogInComponent implements OnInit, OnDestroy {
183165
ngOnDestroy(): void {
184166
this.modalRef?.close();
185167
}
168+
169+
/**
170+
* Generate the information text to be displayed when the user has no email
171+
* @param authMethod the registration type
172+
*/
173+
private generateInformationTextWhenNOEmail(authMethod: string): string {
174+
if (authMethod) {
175+
const authMethodUppercase = authMethod.toUpperCase();
176+
return this.translate.instant('external-login.noEmail.informationText', {
177+
authMethod: authMethodUppercase,
178+
});
179+
}
180+
}
181+
182+
/**
183+
* Generate the information text to be displayed when the user has an email
184+
* @param authMethod the registration type
185+
*/
186+
private generateInformationTextWhenEmail(authMethod: string): string {
187+
if (authMethod) {
188+
const authMethodUppercase = authMethod.toUpperCase();
189+
return this.translate.instant(
190+
'external-login.haveEmail.informationText',
191+
{ authMethod: authMethodUppercase },
192+
);
193+
}
194+
}
186195
}

src/app/external-login-review-account-info-page/review-account-info/review-account-info.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ <h2> {{'external-login-validation.review-account-info.header' | translate}}</h2>
2323
<td>{{ registrationData.netId }}</td>
2424
<td>
2525
<span>
26-
{{ 'external-login-validation.review-account-info.table.row.not-applicable' }}
26+
{{ 'external-login-validation.review-account-info.table.row.not-applicable' | translate }}
2727
</span>
2828
</td>
2929
<td></td>
@@ -55,7 +55,7 @@ <h2> {{'external-login-validation.review-account-info.header' | translate}}</h2>
5555
</tbody>
5656
</table> <div class="d-flex justify-content-end">
5757
<button class="btn btn-primary" (click)="onSave()">
58-
{{'confirmation-modal.review-account-info.confirm' | translate}}
58+
{{'confirmation-modal.review-account-info.save' | translate}}
5959
</button>
6060
</div>
6161
</div>

0 commit comments

Comments
 (0)