// SPDX-License-Identifier: AGPL-3.0
import { Dispatch } from "redux";
-import { RootState } from "~/store/store";
-import { ServiceRepository } from "~/services/services";
-import { setBreadcrumbs } from "~/store/breadcrumbs/breadcrumbs-actions";
-import { snackbarActions, SnackbarKind } from "~/store/snackbar/snackbar-actions";
-import { LinkAccountType, AccountToLink } from "~/models/link-account";
-import { saveApiToken, saveUser } from "~/store/auth/auth-action";
-import { unionize, ofType, UnionOf } from '~/common/unionize';
-import { UserResource } from "~/models/user";
-import { GroupResource } from "~/models/group";
+import { RootState } from "store/store";
+import { getUserUuid } from "common/getuser";
+import { ServiceRepository, createServices, setAuthorizationHeader } from "services/services";
+import { setBreadcrumbs } from "store/breadcrumbs/breadcrumbs-actions";
+import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions";
+import { LinkAccountType, AccountToLink, LinkAccountStatus } from "models/link-account";
+import { authActions, getConfig } from "store/auth/auth-action";
+import { unionize, ofType, UnionOf } from 'common/unionize';
+import { UserResource } from "models/user";
+import { GroupResource } from "models/group";
import { LinkAccountPanelError, OriginatingUser } from "./link-account-panel-reducer";
-import { navigateToRootProject } from "../navigation/navigation-action";
+import { login, logout } from "store/auth/auth-action";
+import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
+import { WORKBENCH_LOADING_SCREEN } from 'store/workbench/workbench-actions';
export const linkAccountPanelActions = unionize({
- LINK_INIT: ofType<{ targetUser: UserResource | undefined }>(),
+ LINK_INIT: ofType<{
+ targetUser: UserResource | undefined
+ }>(),
LINK_LOAD: ofType<{
originatingUser: OriginatingUser | undefined,
targetUser: UserResource | undefined,
targetUserToken: string | undefined,
userToLink: UserResource | undefined,
- userToLinkToken: string | undefined }>(),
+ userToLinkToken: string | undefined
+ }>(),
LINK_INVALID: ofType<{
originatingUser: OriginatingUser | undefined,
targetUser: UserResource | undefined,
userToLink: UserResource | undefined,
- error: LinkAccountPanelError }>(),
+ error: LinkAccountPanelError
+ }>(),
+ SET_SELECTED_CLUSTER: ofType<{
+ selectedCluster: string
+ }>(),
+ SET_IS_PROCESSING: ofType<{
+ isProcessing: boolean
+ }>(),
HAS_SESSION_DATA: {}
});
return LinkAccountPanelError.NONE;
}
+const newServices = (dispatch: Dispatch<any>, token: string) => {
+ const config = dispatch<any>(getConfig);
+ const svc = createServices(config, { progressFn: () => { }, errorFn: () => { } });
+ setAuthorizationHeader(svc, token);
+ return svc;
+};
+
+export const checkForLinkStatus = () =>
+ (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+ const status = services.linkAccountService.getLinkOpStatus();
+ if (status !== undefined) {
+ let msg: string;
+ let msgKind: SnackbarKind;
+ if (status.valueOf() === LinkAccountStatus.CANCELLED) {
+ msg = "Account link cancelled!";
+ msgKind = SnackbarKind.INFO;
+ }
+ else if (status.valueOf() === LinkAccountStatus.FAILED) {
+ msg = "Account link failed!";
+ msgKind = SnackbarKind.ERROR;
+ }
+ else if (status.valueOf() === LinkAccountStatus.SUCCESS) {
+ msg = "Account link success!";
+ msgKind = SnackbarKind.SUCCESS;
+ }
+ else {
+ msg = "Unknown Error!";
+ msgKind = SnackbarKind.ERROR;
+ }
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: msg, kind: msgKind, hideDuration: 3000 }));
+ services.linkAccountService.removeLinkOpStatus();
+ }
+ };
+
export const switchUser = (user: UserResource, token: string) =>
(dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
- dispatch(saveUser(user));
- dispatch(saveApiToken(token));
+ dispatch(authActions.INIT_USER({ user, token }));
};
export const linkFailed = () =>
if (linkState.userToLink && linkState.userToLinkToken && linkState.targetUser && linkState.targetUserToken) {
if (linkState.originatingUser === OriginatingUser.TARGET_USER) {
dispatch(switchUser(linkState.targetUser, linkState.targetUserToken));
- dispatch(linkAccountPanelActions.LINK_INIT({targetUser: linkState.targetUser}));
}
else if ((linkState.originatingUser === OriginatingUser.USER_TO_LINK)) {
dispatch(switchUser(linkState.userToLink, linkState.userToLinkToken));
- dispatch(linkAccountPanelActions.LINK_INIT({targetUser: linkState.userToLink}));
}
- dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Account link failed.', kind: SnackbarKind.ERROR , hideDuration: 3000 }));
}
- services.linkAccountService.removeFromSession();
+ services.linkAccountService.removeAccountToLink();
+ services.linkAccountService.saveLinkOpStatus(LinkAccountStatus.FAILED);
+ window.location.reload();
};
export const loadLinkAccountPanel = () =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
- dispatch(setBreadcrumbs([{ label: 'Link account'}]));
- const curUser = getState().auth.user;
- const curToken = getState().auth.apiToken;
- if (curUser && curToken) {
- const curUserResource = await services.userService.get(curUser.uuid);
- const linkAccountData = services.linkAccountService.getFromSession();
-
- // If there is link account session data, then the user has logged in a second time
- if (linkAccountData) {
-
- // If the window is refreshed after the second login, cancel the linking
- if (window.performance) {
- if (performance.navigation.type === PerformanceNavigation.TYPE_BACK_FORWARD ||
- performance.navigation.type === PerformanceNavigation.TYPE_RELOAD) {
- dispatch(cancelLinking());
- return;
+ try {
+ // If there are remote hosts, set the initial selected cluster by getting the first cluster that isn't the local cluster
+ if (getState().linkAccountPanel.selectedCluster === undefined) {
+ const localCluster = getState().auth.localCluster;
+ let selectedCluster = localCluster;
+ for (const key in getState().auth.remoteHosts) {
+ if (key !== localCluster) {
+ selectedCluster = key;
+ break;
}
}
+ dispatch(linkAccountPanelActions.SET_SELECTED_CLUSTER({ selectedCluster }));
+ }
- // Use the token of the user we are getting data for. This avoids any admin/non-admin permissions
- // issues since a user will always be able to query the api server for their own user data.
- dispatch(saveApiToken(linkAccountData.token));
- const savedUserResource = await services.userService.get(linkAccountData.userUuid);
- dispatch(saveApiToken(curToken));
-
- let params: any;
- if (linkAccountData.type === LinkAccountType.ACCESS_OTHER_ACCOUNT) {
- params = {
- originatingUser: OriginatingUser.USER_TO_LINK,
- targetUser: curUserResource,
- targetUserToken: curToken,
- userToLink: savedUserResource,
- userToLinkToken: linkAccountData.token
- };
- }
- else if (linkAccountData.type === LinkAccountType.ADD_OTHER_LOGIN) {
- params = {
- originatingUser: OriginatingUser.TARGET_USER,
- targetUser: savedUserResource,
- targetUserToken: linkAccountData.token,
- userToLink: curUserResource,
- userToLinkToken: curToken
- };
- }
- else {
- // This should never really happen, but just in case, switch to the user that
- // originated the linking operation (i.e. the user saved in session data)
- dispatch(switchUser(savedUserResource, linkAccountData.token));
- dispatch(linkAccountPanelActions.LINK_INIT({targetUser: savedUserResource}));
- throw new Error("Invalid link account type.");
- }
+ // First check if an account link operation has completed
+ dispatch(checkForLinkStatus());
+
+ // Continue loading the link account panel
+ dispatch(setBreadcrumbs([{ label: 'Link account' }]));
+ const curUser = getState().auth.user;
+ const curToken = getState().auth.apiToken;
+ if (curUser && curToken) {
+
+ // If there is link account session data, then the user has logged in a second time
+ const linkAccountData = services.linkAccountService.getAccountToLink();
+ if (linkAccountData) {
+
+ dispatch(linkAccountPanelActions.SET_IS_PROCESSING({ isProcessing: true }));
+ const curUserResource = await services.userService.get(curUser.uuid);
+
+ // Use the token of the user we are getting data for. This avoids any admin/non-admin permissions
+ // issues since a user will always be able to query the api server for their own user data.
+ const svc = newServices(dispatch, linkAccountData.token);
+ const savedUserResource = await svc.userService.get(linkAccountData.userUuid);
- dispatch(switchUser(params.targetUser, params.targetUserToken));
- const error = validateLink(params.userToLink, params.targetUser);
- if (error === LinkAccountPanelError.NONE) {
- dispatch(linkAccountPanelActions.LINK_LOAD(params));
+ let params: any;
+ if (linkAccountData.type === LinkAccountType.ACCESS_OTHER_ACCOUNT || linkAccountData.type === LinkAccountType.ACCESS_OTHER_REMOTE_ACCOUNT) {
+ params = {
+ originatingUser: OriginatingUser.USER_TO_LINK,
+ targetUser: curUserResource,
+ targetUserToken: curToken,
+ userToLink: savedUserResource,
+ userToLinkToken: linkAccountData.token
+ };
+ }
+ else if (linkAccountData.type === LinkAccountType.ADD_OTHER_LOGIN || linkAccountData.type === LinkAccountType.ADD_LOCAL_TO_REMOTE) {
+ params = {
+ originatingUser: OriginatingUser.TARGET_USER,
+ targetUser: savedUserResource,
+ targetUserToken: linkAccountData.token,
+ userToLink: curUserResource,
+ userToLinkToken: curToken
+ };
+ }
+ else {
+ throw new Error("Unknown link account type");
+ }
+
+ dispatch(switchUser(params.targetUser, params.targetUserToken));
+ const error = validateLink(params.userToLink, params.targetUser);
+ if (error === LinkAccountPanelError.NONE) {
+ dispatch(linkAccountPanelActions.LINK_LOAD(params));
+ }
+ else {
+ dispatch(linkAccountPanelActions.LINK_INVALID({
+ originatingUser: params.originatingUser,
+ targetUser: params.targetUser,
+ userToLink: params.userToLink,
+ error
+ }));
+ return;
+ }
}
else {
- dispatch(linkAccountPanelActions.LINK_INVALID({
- originatingUser: params.originatingUser,
- targetUser: params.targetUser,
- userToLink: params.userToLink,
- error}));
+ // If there is no link account session data, set the state to invoke the initial UI
+ const curUserResource = await services.userService.get(curUser.uuid);
+ dispatch(linkAccountPanelActions.LINK_INIT({ targetUser: curUserResource }));
return;
}
}
- else {
- // If there is no link account session data, set the state to invoke the initial UI
- dispatch(linkAccountPanelActions.LINK_INIT({ targetUser: curUserResource }));
- return;
- }
+ }
+ catch (e) {
+ dispatch(linkFailed());
+ }
+ finally {
+ dispatch(linkAccountPanelActions.SET_IS_PROCESSING({ isProcessing: false }));
}
};
-export const saveAccountLinkData = (t: LinkAccountType) =>
+export const startLinking = (t: LinkAccountType) =>
(dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
- const accountToLink = {type: t, userUuid: services.authService.getUuid(), token: services.authService.getApiToken()} as AccountToLink;
- services.linkAccountService.saveToSession(accountToLink);
+ const userUuid = getUserUuid(getState());
+ if (!userUuid) { return; }
+ const accountToLink = { type: t, userUuid, token: services.authService.getApiToken() } as AccountToLink;
+ services.linkAccountService.saveAccountToLink(accountToLink);
+
const auth = getState().auth;
- services.authService.login(auth.localCluster, auth.remoteHosts[auth.homeCluster]);
+ const isLocalUser = auth.user!.uuid.substring(0, 5) === auth.localCluster;
+ let homeCluster = auth.localCluster;
+ if (isLocalUser && t === LinkAccountType.ACCESS_OTHER_REMOTE_ACCOUNT) {
+ homeCluster = getState().linkAccountPanel.selectedCluster!;
+ }
+
+ dispatch(logout());
+ dispatch(login(auth.localCluster, homeCluster, auth.loginCluster, auth.remoteHosts));
};
export const getAccountLinkData = () =>
(dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
- return services.linkAccountService.getFromSession();
+ return services.linkAccountService.getAccountToLink();
};
-export const cancelLinking = () =>
+export const cancelLinking = (reload: boolean = false) =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
let user: UserResource | undefined;
try {
// When linking is cancelled switch to the originating user (i.e. the user saved in session data)
- const linkAccountData = services.linkAccountService.getFromSession();
+ dispatch(progressIndicatorActions.START_WORKING(WORKBENCH_LOADING_SCREEN));
+ const linkAccountData = services.linkAccountService.getAccountToLink();
if (linkAccountData) {
- dispatch(saveApiToken(linkAccountData.token));
- user = await services.userService.get(linkAccountData.userUuid);
+ services.linkAccountService.removeAccountToLink();
+ const svc = newServices(dispatch, linkAccountData.token);
+ user = await svc.userService.get(linkAccountData.userUuid);
dispatch(switchUser(user, linkAccountData.token));
+ services.linkAccountService.saveLinkOpStatus(LinkAccountStatus.CANCELLED);
}
}
finally {
- services.linkAccountService.removeFromSession();
- dispatch(linkAccountPanelActions.LINK_INIT({ targetUser: user }));
+ if (reload) {
+ window.location.reload();
+ }
+ else {
+ dispatch(progressIndicatorActions.STOP_WORKING(WORKBENCH_LOADING_SCREEN));
+ }
}
};
try {
// The merge api links the user sending the request into the user
- // specified in the request, so switch users for this api call
- dispatch(switchUser(linkState.userToLink, linkState.userToLinkToken));
- await services.linkAccountService.linkAccounts(linkState.targetUserToken, newGroup.uuid);
+ // specified in the request, so change the authorization header accordingly
+ const svc = newServices(dispatch, linkState.userToLinkToken);
+ await svc.linkAccountService.linkAccounts(linkState.targetUserToken, newGroup.uuid);
dispatch(switchUser(linkState.targetUser, linkState.targetUserToken));
- dispatch(navigateToRootProject);
- dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Account link success!', kind: SnackbarKind.SUCCESS, hideDuration: 3000 }));
- dispatch(linkAccountPanelActions.LINK_INIT({targetUser: linkState.targetUser}));
+ services.linkAccountService.removeAccountToLink();
+ services.linkAccountService.saveLinkOpStatus(LinkAccountStatus.SUCCESS);
+ window.location.reload();
}
- catch(e) {
+ catch (e) {
// If the link operation fails, delete the previously made project
try {
- dispatch(switchUser(linkState.targetUser, linkState.targetUserToken));
- await services.projectService.delete(newGroup.uuid);
+ const svc = newServices(dispatch, linkState.targetUserToken);
+ await svc.projectService.delete(newGroup.uuid);
}
finally {
dispatch(linkFailed());
}
throw e;
}
- finally {
- services.linkAccountService.removeFromSession();
- }
}
- };
\ No newline at end of file
+ };