1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { Middleware } from "redux";
6 import { authActions, } from "./auth-action";
7 import { ServiceRepository, setAuthorizationHeader, removeAuthorizationHeader } from "services/services";
8 import { initSessions } from "store/auth/auth-action-session";
9 import { User } from "models/user";
10 import { RootState } from 'store/store';
11 import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
12 import { WORKBENCH_LOADING_SCREEN } from 'store/workbench/workbench-actions';
13 import { navigateToMyAccount } from 'store/navigation/navigation-action';
15 export const authMiddleware = (services: ServiceRepository): Middleware => store => next => action => {
16 // Middleware to update external state (local storage, window
17 // title) to ensure that they stay in sync with redux state.
19 authActions.match(action, {
20 INIT_USER: ({ user, token }) => {
21 // The "next" method passes the action to the next
22 // middleware in the chain, or the reducer. That means
23 // after next() returns, the action has (presumably) been
24 // applied by the reducer to update the state.
27 const state: RootState = store.getState();
29 if (state.auth.apiToken) {
30 services.authService.saveApiToken(state.auth.apiToken);
31 setAuthorizationHeader(services, state.auth.apiToken);
33 services.authService.removeApiToken();
34 services.authService.removeSessions();
35 removeAuthorizationHeader(services);
38 store.dispatch<any>(initSessions(services.authService, state.auth.remoteHostsConfig[state.auth.localCluster], user));
39 if (Object.keys(state.auth.config.clusterConfig.Workbench.UserProfileFormFields).length > 0 &&
41 (Object.keys(user.prefs).length === 0 ||
42 user.prefs.profile === undefined ||
43 Object.keys(user.prefs.profile!).length === 0)) {
44 // If the user doesn't have a profile set, send them
45 // to the user profile page to encourage them to fill it out.
46 store.dispatch(navigateToMyAccount);
49 // As a special case, if the user is inactive, they
50 // may be able to self-activate using the "activate"
51 // method. Note, for this to work there can't be any
52 // unsigned user agreements, we assume the API server is just going to
53 // rubber-stamp our activation request. At some point in the future we'll
54 // want to either add support for displaying/signing user
55 // agreements or get rid of self-activation.
56 // For more details, see:
57 // https://doc.arvados.org/main/admin/user-management.html
59 store.dispatch(progressIndicatorActions.START_WORKING(WORKBENCH_LOADING_SCREEN));
60 services.userService.activate(user.uuid).then((user: User) => {
61 store.dispatch(authActions.INIT_USER({ user, token }));
62 store.dispatch(progressIndicatorActions.STOP_WORKING(WORKBENCH_LOADING_SCREEN));
64 store.dispatch(progressIndicatorActions.STOP_WORKING(WORKBENCH_LOADING_SCREEN));
68 SET_CONFIG: ({ config }) => {
69 document.title = `Arvados (${config.uuidPrefix})`;
72 LOGOUT: ({ deleteLinkData, preservePath }) => {
75 services.linkAccountService.removeAccountToLink();
77 const token = services.authService.getApiToken();
78 services.authService.removeApiToken();
79 services.authService.removeSessions();
80 services.authService.removeUser();
81 removeAuthorizationHeader(services);
82 services.authService.logout(token || '', preservePath);
84 default: () => next(action)