1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { Dispatch } from "redux";
6 import { bindDataExplorerActions } from 'store/data-explorer/data-explorer-action';
7 import { RootState } from 'store/store';
8 import { getUserUuid } from "common/getuser";
9 import { ServiceRepository } from "services/services";
10 import { dialogActions } from 'store/dialog/dialog-actions';
11 import { startSubmit, reset, stopSubmit } from "redux-form";
12 import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
13 import { UserResource } from "models/user";
14 import { filterResources, getResource } from 'store/resources/resources';
15 import { navigateTo, navigateToUsers, navigateToRootProject } from "store/navigation/navigation-action";
16 import { authActions } from 'store/auth/auth-action';
17 import { getTokenV2 } from "models/api-client-authorization";
18 import { VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD, VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD } from "store/virtual-machines/virtual-machines-actions";
19 import { PermissionLevel } from "models/permission";
20 import { updateResources } from "store/resources/resources-actions";
21 import { BuiltinGroups, getBuiltinGroupUuid } from "models/group";
22 import { LinkClass, LinkResource } from "models/link";
23 import { ResourceKind } from "models/resource";
25 export const USERS_PANEL_ID = 'usersPanel';
26 export const USER_ATTRIBUTES_DIALOG = 'userAttributesDialog';
27 export const USER_CREATE_FORM_NAME = 'userCreateFormName';
29 export interface UserCreateFormDialogData {
31 [VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD]: string;
32 [VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD]: string[];
35 export const userBindedActions = bindDataExplorerActions(USERS_PANEL_ID);
37 export const openUserAttributes = (uuid: string) =>
38 (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
39 const { resources } = getState();
40 const data = getResource<UserResource>(uuid)(resources);
41 dispatch(dialogActions.OPEN_DIALOG({ id: USER_ATTRIBUTES_DIALOG, data }));
44 export const loginAs = (uuid: string) =>
45 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
46 const userUuid = getUserUuid(getState());
47 if (userUuid === uuid) {
48 dispatch(snackbarActions.OPEN_SNACKBAR({
49 message: 'You are already logged in as this user',
50 kind: SnackbarKind.WARNING
54 const { resources } = getState();
55 const data = getResource<UserResource>(uuid)(resources);
56 const client = await services.apiClientAuthorizationService.create({ ownerUuid: uuid }, false);
58 dispatch<any>(authActions.INIT_USER({ user: data, token: getTokenV2(client) }));
59 window.location.reload();
60 dispatch<any>(navigateToRootProject);
63 if (e.status === 403) {
64 dispatch(snackbarActions.OPEN_SNACKBAR({
65 message: 'You do not have permission to login as this user',
66 kind: SnackbarKind.WARNING
69 dispatch(snackbarActions.OPEN_SNACKBAR({
70 message: 'Failed to login as this user',
71 kind: SnackbarKind.ERROR
78 export const openUserCreateDialog = () =>
79 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
80 const userUuid = getUserUuid(getState());
81 if (!userUuid) { return; }
82 const user = await services.userService.get(userUuid!);
83 const virtualMachines = await services.virtualMachineService.list();
84 dispatch(reset(USER_CREATE_FORM_NAME));
85 dispatch(dialogActions.OPEN_DIALOG({ id: USER_CREATE_FORM_NAME, data: { user, ...virtualMachines } }));
88 export const openUserProjects = (uuid: string) =>
89 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
90 dispatch<any>(navigateTo(uuid));
93 export const createUser = (data: UserCreateFormDialogData) =>
94 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
95 dispatch(startSubmit(USER_CREATE_FORM_NAME));
97 const newUser = await services.userService.create({
100 dispatch(updateResources([newUser]));
102 if (data[VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD]) {
103 const permission = await services.permissionService.create({
104 headUuid: data[VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD],
105 tailUuid: newUser.uuid,
106 name: PermissionLevel.CAN_LOGIN,
108 username: newUser.username,
112 dispatch(updateResources([permission]));
115 dispatch(dialogActions.CLOSE_DIALOG({ id: USER_CREATE_FORM_NAME }));
116 dispatch(reset(USER_CREATE_FORM_NAME));
117 dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been successfully created.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
118 dispatch<any>(loadUsersPanel());
119 dispatch(userBindedActions.REQUEST_ITEMS());
124 dispatch(stopSubmit(USER_CREATE_FORM_NAME));
128 export const openUserPanel = () =>
129 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
130 const user = getState().auth.user;
131 if (user && user.isAdmin) {
132 dispatch<any>(navigateToUsers);
134 dispatch<any>(navigateToRootProject);
135 dispatch(snackbarActions.OPEN_SNACKBAR({ message: "You don't have permissions to view this page", hideDuration: 2000 }));
139 export const loadUsersPanel = () =>
140 (dispatch: Dispatch) => {
141 dispatch(userBindedActions.RESET_EXPLORER_SEARCH_VALUE());
142 dispatch(userBindedActions.REQUEST_ITEMS());
145 export enum UserAccountStatus {
147 INACTIVE = 'Inactive',
152 export const getUserAccountStatus = (state: RootState, uuid: string) => {
153 const user = getResource<UserResource>(uuid)(state.resources);
154 // Get membership links for all users group
155 const allUsersGroupUuid = getBuiltinGroupUuid(state.auth.localCluster, BuiltinGroups.ALL);
156 const permissions = filterResources((resource: LinkResource) =>
157 resource.kind === ResourceKind.LINK &&
158 resource.linkClass === LinkClass.PERMISSION &&
159 resource.headUuid === allUsersGroupUuid &&
160 resource.tailUuid === uuid
163 return user && user.isActive
164 ? UserAccountStatus.ACTIVE
165 : permissions.length > 0
166 ? UserAccountStatus.SETUP
167 : UserAccountStatus.INACTIVE;