Skip to content

Commit b1605e0

Browse files
authored
Merge branch 'main' into DS-8636
2 parents 9de0021 + 4847fc6 commit b1605e0

45 files changed

Lines changed: 404 additions & 285 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ jobs:
6868
# https://github.com/actions/cache/blob/main/examples.md#node---yarn
6969
- name: Get Yarn cache directory
7070
id: yarn-cache-dir-path
71-
run: echo "::set-output name=dir::$(yarn cache dir)"
71+
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
7272
- name: Cache Yarn dependencies
7373
uses: actions/cache@v3
7474
with:
@@ -93,12 +93,16 @@ jobs:
9393
- name: Run specs (unit tests)
9494
run: yarn run test:headless
9595

96+
# Upload code coverage report to artifact (for one version of Node only),
97+
# so that it can be shared with the 'codecov' job (see below)
9698
# NOTE: Angular CLI only supports code coverage for specs. See https://github.com/angular/angular-cli/issues/6286
97-
# Upload coverage reports to Codecov (for one version of Node only)
98-
# https://github.com/codecov/codecov-action
99-
- name: Upload coverage to Codecov.io
100-
uses: codecov/codecov-action@v3
101-
if: matrix.node-version == '16.x'
99+
- name: Upload code coverage report to Artifact
100+
uses: actions/upload-artifact@v3
101+
if: matrix.node-version == '18.x'
102+
with:
103+
name: dspace-angular coverage report
104+
path: 'coverage/dspace-angular/lcov.info'
105+
retention-days: 14
102106

103107
# Using docker-compose start backend using CI configuration
104108
# and load assetstore from a cached copy
@@ -112,11 +116,10 @@ jobs:
112116
# https://github.com/cypress-io/github-action
113117
# (NOTE: to run these e2e tests locally, just use 'ng e2e')
114118
- name: Run e2e tests (integration tests)
115-
uses: cypress-io/github-action@v4
119+
uses: cypress-io/github-action@v5
116120
with:
117-
# Run tests in Chrome, headless mode
121+
# Run tests in Chrome, headless mode (default)
118122
browser: chrome
119-
headless: true
120123
# Start app before running tests (will be stopped automatically after tests finish)
121124
start: yarn run serve:ssr
122125
# Wait for backend & frontend to be available
@@ -176,3 +179,32 @@ jobs:
176179

177180
- name: Shutdown Docker containers
178181
run: docker-compose -f ./docker/docker-compose-ci.yml down
182+
183+
# Codecov upload is a separate job in order to allow us to restart this separate from the entire build/test
184+
# job above. This is necessary because Codecov uploads seem to randomly fail at times.
185+
# See https://community.codecov.com/t/upload-issues-unable-to-locate-build-via-github-actions-api/3954
186+
codecov:
187+
# Must run after 'tests' job above
188+
needs: tests
189+
runs-on: ubuntu-latest
190+
steps:
191+
- name: Checkout
192+
uses: actions/checkout@v3
193+
194+
# Download artifacts from previous 'tests' job
195+
- name: Download coverage artifacts
196+
uses: actions/download-artifact@v3
197+
198+
# Now attempt upload to Codecov using its action.
199+
# NOTE: We use a retry action to retry the Codecov upload if it fails the first time.
200+
#
201+
# Retry action: https://github.com/marketplace/actions/retry-action
202+
# Codecov action: https://github.com/codecov/codecov-action
203+
- name: Upload coverage to Codecov.io
204+
uses: Wandalen/wretry.action@v1.0.36
205+
with:
206+
action: codecov/codecov-action@v3
207+
# Try upload 5 times max
208+
attempt_limit: 5
209+
# Run again in 30 seconds
210+
attempt_delay: 30000

.github/workflows/issue_opened.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
# Only add to project board if issue is flagged as "needs triage" or has no labels
1717
# NOTE: By default we flag new issues as "needs triage" in our issue template
1818
if: (contains(github.event.issue.labels.*.name, 'needs triage') || join(github.event.issue.labels.*.name) == '')
19-
uses: actions/add-to-project@v0.3.0
19+
uses: actions/add-to-project@v0.5.0
2020
# Note, the authentication token below is an ORG level Secret.
2121
# It must be created/recreated manually via a personal access token with admin:org, project, public_repo permissions
2222
# See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token#permissions-for-the-github_token

.github/workflows/label_merge_conflicts.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
steps:
2424
# See: https://github.com/prince-chrismc/label-merge-conflicts-action
2525
- name: Auto-label PRs with merge conflicts
26-
uses: prince-chrismc/label-merge-conflicts-action@v2
26+
uses: prince-chrismc/label-merge-conflicts-action@v3
2727
# Add "merge conflict" label if a merge conflict is detected. Remove it when resolved.
2828
# Note, the authentication token is created automatically
2929
# See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token

src/app/admin/admin-import-batch-page/batch-import-page.component.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,15 @@ export class BatchImportPageComponent {
9797
this.router.navigateByUrl(getProcessDetailRoute(rd.payload.processId));
9898
}
9999
} else {
100-
const title = this.translate.get('process.new.notification.error.title');
101-
const content = this.translate.get('process.new.notification.error.content');
102-
this.notificationsService.error(title, content);
100+
if (rd.statusCode === 413) {
101+
const title = this.translate.get('process.new.notification.error.title');
102+
const content = this.translate.get('process.new.notification.error.max-upload.content');
103+
this.notificationsService.error(title, content);
104+
} else {
105+
const title = this.translate.get('process.new.notification.error.title');
106+
const content = this.translate.get('process.new.notification.error.content');
107+
this.notificationsService.error(title, content);
108+
}
103109
}
104110
});
105111
}

src/app/core/auth/auth.actions.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const AuthActionTypes = {
1717
AUTHENTICATED_SUCCESS: type('dspace/auth/AUTHENTICATED_SUCCESS'),
1818
CHECK_AUTHENTICATION_TOKEN: type('dspace/auth/CHECK_AUTHENTICATION_TOKEN'),
1919
CHECK_AUTHENTICATION_TOKEN_COOKIE: type('dspace/auth/CHECK_AUTHENTICATION_TOKEN_COOKIE'),
20+
SET_AUTH_COOKIE_STATUS: type('dspace/auth/SET_AUTH_COOKIE_STATUS'),
2021
RETRIEVE_AUTH_METHODS: type('dspace/auth/RETRIEVE_AUTH_METHODS'),
2122
RETRIEVE_AUTH_METHODS_SUCCESS: type('dspace/auth/RETRIEVE_AUTH_METHODS_SUCCESS'),
2223
RETRIEVE_AUTH_METHODS_ERROR: type('dspace/auth/RETRIEVE_AUTH_METHODS_ERROR'),
@@ -150,6 +151,19 @@ export class CheckAuthenticationTokenCookieAction implements Action {
150151
public type: string = AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE;
151152
}
152153

154+
/**
155+
* Sets the authentication cookie status to flag an external authentication response.
156+
*/
157+
export class SetAuthCookieStatus implements Action {
158+
public type: string = AuthActionTypes.SET_AUTH_COOKIE_STATUS;
159+
160+
payload = false;
161+
162+
constructor(exists: boolean) {
163+
this.payload = exists;
164+
}
165+
}
166+
153167
/**
154168
* Sign out.
155169
* @class LogOutAction
@@ -425,6 +439,7 @@ export type AuthActions
425439
| AuthenticationSuccessAction
426440
| CheckAuthenticationTokenAction
427441
| CheckAuthenticationTokenCookieAction
442+
| SetAuthCookieStatus
428443
| RedirectWhenAuthenticationIsRequiredAction
429444
| RedirectWhenTokenExpiredAction
430445
| AddAuthenticationMessageAction

src/app/core/auth/auth.effects.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,15 @@ describe('AuthEffects', () => {
214214
authenticated: true
215215
})
216216
);
217+
spyOn((authEffects as any).authService, 'setExternalAuthStatus');
217218
actions = hot('--a-', { a: { type: AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE } });
218219

219220
const expected = cold('--b-', { b: new RetrieveTokenAction() });
220221

221222
expect(authEffects.checkTokenCookie$).toBeObservable(expected);
222223
authEffects.checkTokenCookie$.subscribe(() => {
224+
expect(authServiceStub.setExternalAuthStatus).toHaveBeenCalled();
225+
expect(authServiceStub.isExternalAuthentication).toBeTrue();
223226
expect((authEffects as any).authorizationsService.invalidateAuthorizationsRequestCache).toHaveBeenCalled();
224227
});
225228
});

src/app/core/auth/auth.effects.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ export class AuthEffects {
153153
return this.authService.checkAuthenticationCookie().pipe(
154154
map((response: AuthStatus) => {
155155
if (response.authenticated) {
156+
this.authService.setExternalAuthStatus(true);
156157
this.authorizationsService.invalidateAuthorizationsRequestCache();
157158
return new RetrieveTokenAction();
158159
} else {

src/app/core/auth/auth.reducer.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
AuthenticationErrorAction,
99
AuthenticationSuccessAction,
1010
CheckAuthenticationTokenAction,
11+
SetAuthCookieStatus,
1112
CheckAuthenticationTokenCookieAction,
1213
LogOutAction,
1314
LogOutErrorAction,
@@ -219,6 +220,28 @@ describe('authReducer', () => {
219220
expect(newState).toEqual(state);
220221
});
221222

223+
it('should set the authentication cookie status in response to a SET_AUTH_COOKIE_STATUS action', () => {
224+
initialState = {
225+
authenticated: true,
226+
loaded: false,
227+
blocking: false,
228+
loading: true,
229+
externalAuth: false,
230+
idle: false
231+
};
232+
const action = new SetAuthCookieStatus(true);
233+
const newState = authReducer(initialState, action);
234+
state = {
235+
authenticated: true,
236+
loaded: false,
237+
blocking: false,
238+
loading: true,
239+
externalAuth: true,
240+
idle: false
241+
};
242+
expect(newState).toEqual(state);
243+
});
244+
222245
it('should properly set the state, in response to a LOG_OUT action', () => {
223246
initialState = {
224247
authenticated: true,

src/app/core/auth/auth.reducer.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
RedirectWhenTokenExpiredAction,
1111
RefreshTokenSuccessAction,
1212
RetrieveAuthenticatedEpersonSuccessAction,
13-
RetrieveAuthMethodsSuccessAction,
13+
RetrieveAuthMethodsSuccessAction, SetAuthCookieStatus,
1414
SetRedirectUrlAction
1515
} from './auth.actions';
1616
// import models
@@ -59,6 +59,8 @@ export interface AuthState {
5959
// all authentication Methods enabled at the backend
6060
authMethods?: AuthMethod[];
6161

62+
externalAuth?: boolean,
63+
6264
// true when the current user is idle
6365
idle: boolean;
6466

@@ -73,6 +75,7 @@ const initialState: AuthState = {
7375
blocking: true,
7476
loading: false,
7577
authMethods: [],
78+
externalAuth: false,
7679
idle: false
7780
};
7881

@@ -104,6 +107,11 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
104107
loading: true,
105108
});
106109

110+
case AuthActionTypes.SET_AUTH_COOKIE_STATUS:
111+
return Object.assign({}, state, {
112+
externalAuth: (action as SetAuthCookieStatus).payload
113+
});
114+
107115
case AuthActionTypes.AUTHENTICATED_ERROR:
108116
case AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON_ERROR:
109117
return Object.assign({}, state, {

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
import { CookieService } from '../services/cookie.service';
2626
import {
2727
getAuthenticatedUserId,
28-
getAuthenticationToken,
28+
getAuthenticationToken, getExternalAuthCookieStatus,
2929
getRedirectUrl,
3030
isAuthenticated,
3131
isAuthenticatedLoaded,
@@ -36,7 +36,7 @@ import { AppState } from '../../app.reducer';
3636
import {
3737
CheckAuthenticationTokenAction,
3838
RefreshTokenAction,
39-
ResetAuthenticationMessagesAction,
39+
ResetAuthenticationMessagesAction, SetAuthCookieStatus,
4040
SetRedirectUrlAction,
4141
SetUserAsIdleAction,
4242
UnsetUserAsIdleAction
@@ -156,6 +156,24 @@ export class AuthService {
156156
return this.store.pipe(select(isAuthenticatedLoaded));
157157
}
158158

159+
/**
160+
* Used to set the external authentication status when authenticating via an
161+
* external authentication system (e.g. Shibboleth).
162+
* @param external
163+
*/
164+
public setExternalAuthStatus(external: boolean) {
165+
this.store.dispatch(new SetAuthCookieStatus(external));
166+
}
167+
168+
/**
169+
* Returns true if an external authentication system (e.g. Shibboleth) is being used
170+
* for authentication. Returns false otherwise.
171+
*/
172+
public isExternalAuthentication(): Observable<boolean> {
173+
return this.store.pipe(
174+
select(getExternalAuthCookieStatus));
175+
}
176+
159177
/**
160178
* Returns the href link to authenticated user
161179
* @returns {string}

0 commit comments

Comments
 (0)