Login with next, alternative implementation

main
Boris Sekachev 5 years ago
parent e5356cd887
commit 6a779a79fc

@ -40,7 +40,7 @@ export const authActions = {
authorizeSuccess: (user: any) => createAction(AuthActionTypes.AUTHORIZED_SUCCESS, { user }), authorizeSuccess: (user: any) => createAction(AuthActionTypes.AUTHORIZED_SUCCESS, { user }),
authorizeFailed: (error: any) => createAction(AuthActionTypes.AUTHORIZED_FAILED, { error }), authorizeFailed: (error: any) => createAction(AuthActionTypes.AUTHORIZED_FAILED, { error }),
login: () => createAction(AuthActionTypes.LOGIN), login: () => createAction(AuthActionTypes.LOGIN),
loginSuccess: (user: any) => createAction(AuthActionTypes.LOGIN_SUCCESS, { user }), loginSuccess: (user: any, next: string | null) => createAction(AuthActionTypes.LOGIN_SUCCESS, { user, next }),
loginFailed: (error: any) => createAction(AuthActionTypes.LOGIN_FAILED, { error }), loginFailed: (error: any) => createAction(AuthActionTypes.LOGIN_FAILED, { error }),
register: () => createAction(AuthActionTypes.REGISTER), register: () => createAction(AuthActionTypes.REGISTER),
registerSuccess: (user: any) => createAction(AuthActionTypes.REGISTER_SUCCESS, { user }), registerSuccess: (user: any) => createAction(AuthActionTypes.REGISTER_SUCCESS, { user }),
@ -98,14 +98,16 @@ export const registerAsync = (
} }
}; };
export const loginAsync = (username: string, password: string): ThunkAction => async (dispatch) => { export const loginAsync = (username: string, password: string, next: string | null): ThunkAction => async (
dispatch,
) => {
dispatch(authActions.login()); dispatch(authActions.login());
try { try {
await cvat.server.login(username, password); await cvat.server.login(username, password);
const users = await cvat.users.get({ self: true }); const users = await cvat.users.get({ self: true });
dispatch(authActions.loginSuccess(users[0])); dispatch(authActions.loginSuccess(users[0], next));
} catch (error) { } catch (error) {
dispatch(authActions.loginFailed(error)); dispatch(authActions.loginFailed(error));
} }

@ -64,6 +64,7 @@ interface CVATAppProps {
authActionsInitialized: boolean; authActionsInitialized: boolean;
notifications: NotificationsState; notifications: NotificationsState;
user: any; user: any;
next: string | null;
isModelPluginActive: boolean; isModelPluginActive: boolean;
} }
@ -231,16 +232,19 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP
aboutInitialized, aboutInitialized,
pluginsInitialized, pluginsInitialized,
formatsInitialized, formatsInitialized,
modelsInitialized,
switchShortcutsDialog, switchShortcutsDialog,
switchSettingsDialog, switchSettingsDialog,
user, user,
next,
keyMap, keyMap,
location,
isModelPluginActive, isModelPluginActive,
} = this.props; } = this.props;
const readyForRender = const readyForRender =
(userInitialized && (user == null || !user.isVerified)) || (userInitialized && (user == null || !user.isVerified)) ||
(userInitialized && formatsInitialized && pluginsInitialized && aboutInitialized); (userInitialized && formatsInitialized && pluginsInitialized && aboutInitialized && modelsInitialized);
const subKeyMap = { const subKeyMap = {
SWITCH_SHORTCUTS: keyMap.SWITCH_SHORTCUTS, SWITCH_SHORTCUTS: keyMap.SWITCH_SHORTCUTS,
@ -310,7 +314,7 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP
{isModelPluginActive && ( {isModelPluginActive && (
<Route exact path='/models' component={ModelsPageContainer} /> <Route exact path='/models' component={ModelsPageContainer} />
)} )}
<Redirect push to='/tasks' /> <Redirect push to={next || '/tasks'} />
</Switch> </Switch>
</GlobalHotKeys> </GlobalHotKeys>
{/* eslint-disable-next-line */} {/* eslint-disable-next-line */}
@ -337,7 +341,9 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP
path='/auth/password/reset/confirm' path='/auth/password/reset/confirm'
component={ResetPasswordPageConfirmComponent} component={ResetPasswordPageConfirmComponent}
/> />
<Redirect to='/auth/login' /> <Redirect
to={location.pathname.length > 1 ? `/auth/login/?next=${location.pathname}` : '/auth/login'}
/>
</Switch> </Switch>
</GlobalErrorBoundary> </GlobalErrorBoundary>
); );

@ -15,7 +15,8 @@ import CookieDrawer from './cookie-policy-drawer';
interface LoginPageComponentProps { interface LoginPageComponentProps {
fetching: boolean; fetching: boolean;
renderResetPassword: boolean; renderResetPassword: boolean;
onLogin: (username: string, password: string) => void; next: string;
onLogin: (username: string, password: string, next: string | null) => void;
} }
function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps): JSX.Element { function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps): JSX.Element {
@ -27,7 +28,11 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps
xl: { span: 4 }, xl: { span: 4 },
}; };
const { fetching, onLogin, renderResetPassword } = props; const {
fetching, onLogin, renderResetPassword, location,
} = props;
const search = new URLSearchParams(location.search);
return ( return (
<> <>
@ -37,7 +42,7 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps
<LoginForm <LoginForm
fetching={fetching} fetching={fetching}
onSubmit={(loginData: LoginData): void => { onSubmit={(loginData: LoginData): void => {
onLogin(loginData.username, loginData.password); onLogin(loginData.username, loginData.password, search.get('next'));
}} }}
/> />
<Row type='flex' justify='start' align='top'> <Row type='flex' justify='start' align='top'>

@ -3,15 +3,17 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { Redirect, useParams } from 'react-router'; import { Redirect, useParams, useLocation } from 'react-router';
import { useCookies } from 'react-cookie'; import { useCookies } from 'react-cookie';
export default function LoginWithTokenComponent(): JSX.Element { export default function LoginWithTokenComponent(): JSX.Element {
const { sessionId, token } = useParams(); const location = useLocation();
const { sessionId, token } = useParams<{ sessionId: string; token: string }>();
const [cookies, setCookie] = useCookies(['sessionid', 'csrftoken']); const [cookies, setCookie] = useCookies(['sessionid', 'csrftoken']);
const expires1y = new Date(new Date().setFullYear(new Date().getFullYear() + 1)); const expires1y = new Date(new Date().setFullYear(new Date().getFullYear() + 1));
const expires2w = new Date(new Date().setDate(new Date().getDate() + 13)); const expires2w = new Date(new Date().setDate(new Date().getDate() + 13));
const search = new URLSearchParams(location.search);
setCookie('sessionid', sessionId, { path: '/', expires: expires2w }); setCookie('sessionid', sessionId, { path: '/', expires: expires2w });
setCookie('csrftoken', token, { path: '/', expires: expires1y }); setCookie('csrftoken', token, { path: '/', expires: expires1y });
@ -24,7 +26,7 @@ export default function LoginWithTokenComponent(): JSX.Element {
); );
if (cookies.sessionid && cookies.csrftoken) { if (cookies.sessionid && cookies.csrftoken) {
return <Redirect to='/tasks' />; return <Redirect to={search.has('next') ? (search.get('next') as string) : '/tasks'} />;
} }
return <></>; return <></>;
} }

@ -45,6 +45,7 @@ interface StateToProps {
allowResetPassword: boolean; allowResetPassword: boolean;
notifications: NotificationsState; notifications: NotificationsState;
user: any; user: any;
next: string | null;
keyMap: Record<string, ExtendedKeyMapOptions>; keyMap: Record<string, ExtendedKeyMapOptions>;
isModelPluginActive: boolean; isModelPluginActive: boolean;
} }
@ -91,6 +92,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
allowResetPassword: auth.allowResetPassword, allowResetPassword: auth.allowResetPassword,
notifications: state.notifications, notifications: state.notifications,
user: auth.user, user: auth.user,
next: auth.next,
keyMap: shortcuts.keyMap, keyMap: shortcuts.keyMap,
isModelPluginActive: plugins.list.MODELS, isModelPluginActive: plugins.list.MODELS,
}; };

@ -10,6 +10,7 @@ const defaultState: AuthState = {
initialized: false, initialized: false,
fetching: false, fetching: false,
user: null, user: null,
next: null,
authActionsFetching: false, authActionsFetching: false,
authActionsInitialized: false, authActionsInitialized: false,
allowChangePassword: false, allowChangePassword: false,
@ -40,6 +41,7 @@ export default function (state = defaultState, action: AuthActions | BoundariesA
...state, ...state,
fetching: false, fetching: false,
user: action.payload.user, user: action.payload.user,
next: action.payload.next,
}; };
case AuthActionTypes.LOGIN_FAILED: case AuthActionTypes.LOGIN_FAILED:
return { return {
@ -94,9 +96,9 @@ export default function (state = defaultState, action: AuthActions | BoundariesA
return { return {
...state, ...state,
showChangePasswordDialog: showChangePasswordDialog:
typeof action.payload.showChangePasswordDialog === 'undefined' typeof action.payload.showChangePasswordDialog === 'undefined' ?
? !state.showChangePasswordDialog !state.showChangePasswordDialog :
: action.payload.showChangePasswordDialog, action.payload.showChangePasswordDialog,
}; };
case AuthActionTypes.REQUEST_PASSWORD_RESET: case AuthActionTypes.REQUEST_PASSWORD_RESET:
return { return {

@ -14,6 +14,7 @@ export interface AuthState {
initialized: boolean; initialized: boolean;
fetching: boolean; fetching: boolean;
user: any; user: any;
next: string | null;
authActionsFetching: boolean; authActionsFetching: boolean;
authActionsInitialized: boolean; authActionsInitialized: boolean;
showChangePasswordDialog: boolean; showChangePasswordDialog: boolean;

Loading…
Cancel
Save