Merge branch '21128-toolbar-context-menu'
[arvados-workbench2.git] / src / store / auth / auth-middleware.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
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';
14
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.
18
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.
25             next(action);
26
27             const state: RootState = store.getState();
28
29             if (state.auth.apiToken) {
30                 services.authService.saveApiToken(state.auth.apiToken);
31                 setAuthorizationHeader(services, state.auth.apiToken);
32             } else {
33                 services.authService.removeApiToken();
34                 services.authService.removeSessions();
35                 removeAuthorizationHeader(services);
36             }
37
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 &&
40                 user.isActive &&
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);
47             }
48             if (!user.isActive) {
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
58
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));
63                 }).catch(() => {
64                     store.dispatch(progressIndicatorActions.STOP_WORKING(WORKBENCH_LOADING_SCREEN));
65                 });
66             }
67         },
68         SET_CONFIG: ({ config }) => {
69             document.title = `Arvados (${config.uuidPrefix})`;
70             next(action);
71         },
72         LOGOUT: ({ deleteLinkData, preservePath }) => {
73             next(action);
74             if (deleteLinkData) {
75                 services.linkAccountService.removeAccountToLink();
76             }
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);
83         },
84         default: () => next(action)
85     });
86 };