Skip to content

Commit

Permalink
Merge branch 'dspace-cris-7' into DSC-1058
Browse files Browse the repository at this point in the history
  • Loading branch information
atarix83 committed Oct 12, 2023
2 parents 3341832 + 26e625a commit b9b2974
Show file tree
Hide file tree
Showing 108 changed files with 4,226 additions and 73 deletions.
15 changes: 15 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,21 @@ import { RedirectService } from './redirect/redirect.service';
loadChildren: () => import('./login-page/login-page.module')
.then((m) => m.LoginPageModule)
},
{
path: 'external-login/:token',
loadChildren: () => import('./external-login-page/external-login-page.module')
.then((m) => m.ExternalLoginPageModule)
},
{
path: 'review-account/:token',
loadChildren: () => import('./external-login-review-account-info-page/external-login-review-account-info-page.module')
.then((m) => m.ExternalLoginReviewAccountInfoModule)
},
{
path: 'email-confirmation',
loadChildren: () => import('./external-login-email-confirmation-page/external-login-email-confirmation-page.module')
.then((m) => m.ExternalLoginEmailConfirmationPageModule)
},
{
path: 'logout',
loadChildren: () => import('./logout-page/logout-page.module')
Expand Down
48 changes: 48 additions & 0 deletions src/app/core/auth/auth.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export const AuthActionTypes = {
RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS: type('dspace/auth/RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS'),
RETRIEVE_AUTHENTICATED_EPERSON_ERROR: type('dspace/auth/RETRIEVE_AUTHENTICATED_EPERSON_ERROR'),
REDIRECT_AFTER_LOGIN_SUCCESS: type('dspace/auth/REDIRECT_AFTER_LOGIN_SUCCESS'),
REFRESH_EPERSON_AND_TOKEN_REDIRECT: type('dspace/auth/REFRESH_EPERSON_AND_TOKEN_REDIRECT'),
REFRESH_EPERSON_AND_TOKEN_REDIRECT_ERROR: type('dspace/auth/REFRESH_EPERSON_AND_TOKEN_REDIRECT_ERROR'),
REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS: type('dspace/auth/REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS'),
REFRESH_TOKEN_AND_REDIRECT: type('dspace/auth/REFRESH_TOKEN_AND_REDIRECT'),
REFRESH_TOKEN_AND_REDIRECT_SUCCESS: type('dspace/auth/REFRESH_TOKEN_AND_REDIRECT_SUCCESS'),
REFRESH_TOKEN_AND_REDIRECT_ERROR: type('dspace/auth/REFRESH_TOKEN_AND_REDIRECT_ERROR'),
Expand Down Expand Up @@ -416,6 +419,51 @@ export class UnsetUserAsIdleAction implements Action {
public type: string = AuthActionTypes.UNSET_USER_AS_IDLE;
}


/**
* Refresh user state, the token and execute a redirect.
* @class RefreshEpersonAndTokenRedirectAction
* @implements {Action}
*/
export class RefreshEpersonAndTokenRedirectAction implements Action {
public type: string = AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT;
payload: {
token: AuthTokenInfo,
redirectUrl: string
};

constructor(token: AuthTokenInfo, redirectUrl: string) {
this.payload = { token, redirectUrl };
}
}

/**
* Refresh user state, the token and execute a redirect.
* @class RefreshEpersonAndTokenRedirectSuccessAction
* @implements {Action}
*/
export class RefreshEpersonAndTokenRedirectSuccessAction implements Action {
public type: string = AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS;
payload: {
ePerson: EPerson,
token: AuthTokenInfo,
redirectUrl: string
};

constructor(ePerson: EPerson, token: AuthTokenInfo, redirectUrl: string) {
this.payload = { ePerson, token, redirectUrl };
}
}

/**
* Refresh user state, the token and execute a redirect.
* @class RefreshEpersonAndTokenRedirectErrorAction
* @implements {Action}
*/
export class RefreshEpersonAndTokenRedirectErrorAction implements Action {
public type: string = AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_ERROR;
}

/**
* Refresh authentication token and redirect.
* @class RefreshTokenAndRedirectAction
Expand Down
65 changes: 65 additions & 0 deletions src/app/core/auth/auth.effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import {
CheckAuthenticationTokenCookieAction,
LogOutErrorAction,
LogOutSuccessAction,
RefreshEpersonAndTokenRedirectErrorAction,
RefreshEpersonAndTokenRedirectSuccessAction,
RefreshTokenAndRedirectAction,
RefreshTokenAndRedirectErrorAction,
RefreshTokenAndRedirectSuccessAction,
RefreshTokenErrorAction,
Expand Down Expand Up @@ -457,6 +460,68 @@ describe('AuthEffects', () => {
});
});

describe('refreshStateTokenRedirect$', () => {

describe('when refresh state, token and redirect action', () => {
it('should return a REFRESH_STATE_TOKEN_AND_REDIRECT_SUCCESS action in response to a REFRESH_STATE_TOKEN_AND_REDIRECT action', (done) => {
spyOn((authEffects as any).authService, 'retrieveAuthenticatedUserById').and.returnValue(observableOf(EPersonMock));

actions = hot('--a-', {
a: {
type: AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT,
payload: { token, redirectUrl }
}
});

const expected = cold('--b-', { b: new RefreshEpersonAndTokenRedirectSuccessAction(EPersonMock, token, redirectUrl) });

expect(authEffects.refreshStateTokenRedirect$).toBeObservable(expected);
done();
});
});

describe('when refresh state token failed', () => {
it('should return a REFRESH_STATE_TOKEN_AND_REDIRECT_SUCCESS action in response to a REFRESH_STATE_TOKEN_AND_REDIRECT action', (done) => {
spyOn((authEffects as any).authService, 'retrieveAuthenticatedUserById').and.returnValue(observableThrow(''));

actions = hot('--a-', {
a: {
type: AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT,
payload: { token, redirectUrl }
}
});

const expected = cold('--b-', { b: new RefreshEpersonAndTokenRedirectErrorAction() });

expect(authEffects.refreshStateTokenRedirect$).toBeObservable(expected);
done();
});
});

});

describe('refreshStateTokenRedirectSuccess$', () => {

beforeEach(() => {
scheduler = getTestScheduler();
});

it('should return a REFRESH_TOKEN_AND_REDIRECT action', (done) => {

actions = hot('--a-', {
a: {
type: AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS,
payload: { ePerson: EPersonMock, token, redirectUrl }
}
});

const expected = cold('--b-', { b: new RefreshTokenAndRedirectAction(token, redirectUrl) });

expect(authEffects.refreshStateTokenRedirectSuccess$).toBeObservable(expected);
done();
});
});

describe('refreshTokenAndRedirect$', () => {

describe('when refresh token and redirect succeeded', () => {
Expand Down
21 changes: 21 additions & 0 deletions src/app/core/auth/auth.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ import {
LogOutErrorAction,
LogOutSuccessAction,
RedirectAfterLoginSuccessAction,
RefreshEpersonAndTokenRedirectAction,
RefreshEpersonAndTokenRedirectErrorAction,
RefreshEpersonAndTokenRedirectSuccessAction,
RefreshTokenAction,
RefreshTokenAndRedirectAction,
RefreshTokenAndRedirectErrorAction,
Expand Down Expand Up @@ -270,6 +273,24 @@ export class AuthEffects {
}))
);

public refreshStateTokenRedirect$: Observable<Action> = createEffect(() => this.actions$
.pipe(ofType(AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT),
switchMap((action: RefreshEpersonAndTokenRedirectAction) =>
this.authService.getAuthenticatedUserFromStore()
.pipe(
switchMap(user => this.authService.retrieveAuthenticatedUserById(user.id)),
map(user => new RefreshEpersonAndTokenRedirectSuccessAction(user, action.payload.token, action.payload.redirectUrl)),
catchError((error) => observableOf(new RefreshEpersonAndTokenRedirectErrorAction()))
)
)
)
);

public refreshStateTokenRedirectSuccess$: Observable<Action> = createEffect(() => this.actions$
.pipe(ofType(AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS),
map((action: RefreshEpersonAndTokenRedirectAction) => new RefreshTokenAndRedirectAction(action.payload.token, action.payload.redirectUrl)))
);

public refreshTokenAndRedirectSuccess$: Observable<Action> = createEffect(() => this.actions$
.pipe(ofType(AuthActionTypes.REFRESH_TOKEN_AND_REDIRECT_SUCCESS),
tap((action: RefreshTokenAndRedirectSuccessAction) => this.authService.replaceToken(action.payload.token)),
Expand Down
20 changes: 20 additions & 0 deletions src/app/core/auth/auth.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
LogOutErrorAction,
RedirectWhenAuthenticationIsRequiredAction,
RedirectWhenTokenExpiredAction,
RefreshEpersonAndTokenRedirectSuccessAction,
RefreshTokenAndRedirectSuccessAction,
RefreshTokenSuccessAction,
RetrieveAuthenticatedEpersonSuccessAction,
Expand Down Expand Up @@ -190,6 +191,25 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
user: undefined
});

case AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT:
return Object.assign({}, state, {
loading: true,
loaded: false,
});

case AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_SUCCESS:
return Object.assign({}, state, {
loading: false,
loaded: false,
user: (action as RefreshEpersonAndTokenRedirectSuccessAction).payload.ePerson,
});

case AuthActionTypes.REFRESH_EPERSON_AND_TOKEN_REDIRECT_ERROR:
return Object.assign({}, state, {
loading: false,
loaded: false,
});

case AuthActionTypes.REFRESH_TOKEN:
return Object.assign({}, state, {
refreshing: true,
Expand Down
26 changes: 26 additions & 0 deletions src/app/core/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { PageInfo } from '../shared/page-info.model';
import { followLink } from '../../shared/utils/follow-link-config.model';
import { MachineToken } from './models/machine-token.model';
import { NoContent } from '../shared/NoContent.model';
import { URLCombiner } from '../url-combiner/url-combiner';

export const LOGIN_ROUTE = '/login';
export const LOGOUT_ROUTE = '/logout';
Expand Down Expand Up @@ -523,6 +524,31 @@ export class AuthService {
});
}

/**
* Returns the external server redirect URL.
* @param origin - The origin route.
* @param redirectRoute - The redirect route.
* @param location - The location.
* @returns The external server redirect URL.
*/
getExternalServerRedirectUrl(origin: string, redirectRoute: string, location: string): string {
const correctRedirectUrl = new URLCombiner(origin, redirectRoute).toString();

let externalServerUrl = location;
const myRegexp = /\?redirectUrl=(.*)/g;
const match = myRegexp.exec(location);
const redirectUrlFromServer = (match && match[1]) ? match[1] : null;

// Check whether the current page is different from the redirect url received from rest
if (isNotNull(redirectUrlFromServer) && redirectUrlFromServer !== correctRedirectUrl) {
// change the redirect url with the current page url
const newRedirectUrl = `?redirectUrl=${correctRedirectUrl}`;
externalServerUrl = location.replace(/\?redirectUrl=(.*)/g, newRedirectUrl);
}

return externalServerUrl;
}

/**
* Clear redirect url
*/
Expand Down
2 changes: 1 addition & 1 deletion src/app/core/auth/models/auth.method-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export enum AuthMethodType {
Ip = 'ip',
X509 = 'x509',
Oidc = 'oidc',
Orcid = 'orcid'
Orcid = 'orcid',
}
4 changes: 4 additions & 0 deletions src/app/core/auth/models/auth.registration-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum AuthRegistrationType {
Orcid = 'ORCID',
Validation = 'VALIDATION_',
}
2 changes: 1 addition & 1 deletion src/app/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ export const models =
WorkflowOwnerStatistics,
LoginStatistics,
Metric,
ItemRequest
ItemRequest,
];

@NgModule({
Expand Down
4 changes: 2 additions & 2 deletions src/app/core/data/eperson-registration.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('EpersonRegistrationService', () => {

describe('searchByToken', () => {
it('should return a registration corresponding to the provided token', () => {
const expected = service.searchByToken('test-token');
const expected = service.searchByTokenAndUpdateData('test-token');

expect(expected).toBeObservable(cold('(a|)', {
a: jasmine.objectContaining({
Expand All @@ -124,7 +124,7 @@ describe('EpersonRegistrationService', () => {
testScheduler.run(({ cold, expectObservable }) => {
rdbService.buildSingle.and.returnValue(cold('a', { a: rd }));

service.searchByToken('test-token');
service.searchByTokenAndUpdateData('test-token');

expect(requestService.send).toHaveBeenCalledWith(
jasmine.objectContaining({
Expand Down
Loading

0 comments on commit b9b2974

Please sign in to comment.