#246: remove logout from user/actions. Use logoutAll from account/actions everywhere

This commit is contained in:
SleepWalker 2017-01-04 07:52:46 +02:00
parent 8e06adbf21
commit 274c28a923
6 changed files with 67 additions and 175 deletions

View File

@ -1,6 +1,8 @@
import { routeActions } from 'react-router-redux';
import authentication from 'services/api/authentication';
import accounts from 'services/api/accounts';
import { updateUser, logout } from 'components/user/actions';
import { updateUser, setGuest } from 'components/user/actions';
import { setLocale } from 'components/i18n/actions';
import logger from 'services/logger';
@ -25,7 +27,7 @@ export function authenticate({token, refreshToken}) {
authentication.validateToken({token, refreshToken})
.catch(() => {
// TODO: log this case
dispatch(logout());
dispatch(logoutAll());
return Promise.reject();
})
@ -80,17 +82,23 @@ export function revoke(account) {
});
}
return dispatch(logout());
return dispatch(logoutAll());
};
}
export function logoutAll() {
return (dispatch, getState) => {
dispatch(setGuest());
const {accounts: {available}} = getState();
available.forEach((account) => authentication.logout(account));
dispatch(reset());
dispatch(routeActions.push('/login'));
return Promise.resolve();
};
}
@ -120,7 +128,7 @@ export function logoutStrangers() {
return dispatch(authenticate(accountToReplace));
}
dispatch(logout());
dispatch(logoutAll());
return Promise.resolve();
};

View File

@ -1,12 +1,16 @@
import { routeActions } from 'react-router-redux';
import { updateUser, logout, acceptRules as userAcceptRules } from 'components/user/actions';
import { authenticate } from 'components/accounts/actions';
import logger from 'services/logger';
import { updateUser, acceptRules as userAcceptRules } from 'components/user/actions';
import { authenticate, logoutAll } from 'components/accounts/actions';
import authentication from 'services/api/authentication';
import oauth from 'services/api/oauth';
import signup from 'services/api/signup';
import dispatchBsod from 'components/ui/bsod/dispatchBsod';
export { updateUser } from 'components/user/actions';
export { authenticate, logoutAll as logout } from 'components/accounts/actions';
export function login({login = '', password = '', rememberMe = false}) {
const PASSWORD_REQUIRED = 'error.password_required';
const LOGIN_REQUIRED = 'error.login_required';
@ -24,9 +28,9 @@ export function login({login = '', password = '', rememberMe = false}) {
} else if (resp.errors.login === ACTIVATION_REQUIRED) {
return dispatch(needActivation());
} else if (resp.errors.login === LOGIN_REQUIRED && password) {
// TODO: log this case to backend
// return to the first step
return dispatch(logout());
logger.warn('No login on password panel');
return dispatch(logoutAll());
}
}
@ -144,9 +148,6 @@ export function clearErrors() {
return setErrors(null);
}
export { logout, updateUser } from 'components/user/actions';
export { authenticate } from 'components/accounts/actions';
/**
* @param {object} oauthData
* @param {string} oauthData.clientId

View File

@ -1,7 +1,4 @@
import { routeActions } from 'react-router-redux';
import accounts from 'services/api/accounts';
import { logoutAll } from 'components/accounts/actions';
import { setLocale } from 'components/i18n/actions';
export const UPDATE = 'USER_UPDATE';
@ -18,6 +15,20 @@ export function updateUser(payload) {
};
}
export const SET = 'USER_SET';
/**
* Replace current user's state with a new one
*
* @param {User} payload
* @return {object} - action definition
*/
export function setUser(payload) {
return {
type: SET,
payload
};
}
export const CHANGE_LANG = 'USER_CHANGE_LANG';
export function changeLang(lang) {
return (dispatch, getState) => dispatch(setLocale(lang))
@ -37,32 +48,12 @@ export function changeLang(lang) {
});
}
export const SET = 'USER_SET';
/**
* Replace current user's state with a new one
*
* @param {User} payload
* @return {object} - action definition
*/
export function setUser(payload) {
return {
type: SET,
payload
};
}
export function logout() {
export function setGuest() {
return (dispatch, getState) => {
dispatch(setUser({
lang: getState().user.lang,
isGuest: true
}));
dispatch(logoutAll());
dispatch(routeActions.push('/login'));
return Promise.resolve();
};
}

View File

@ -1,6 +1,6 @@
import authentication from 'services/api/authentication';
import { updateToken } from 'components/accounts/actions';
import { logout } from '../actions';
import logger from 'services/logger';
import { updateToken, logoutAll } from 'components/accounts/actions';
/**
* Ensures, that all user's requests have fresh access token
@ -41,8 +41,11 @@ export default function refreshTokenMiddleware({dispatch, getState}) {
return requestAccessToken(refreshToken, dispatch).then(() => req);
}
} catch (err) {
// console.error('Bad token', err); // TODO: it would be cool to log such things to backend
return dispatch(logout()).then(() => req);
logger.warn('Refresh token error: bad token', {
token
});
return dispatch(logoutAll()).then(() => req);
}
return Promise.resolve(req);
@ -58,7 +61,7 @@ export default function refreshTokenMiddleware({dispatch, getState}) {
return requestAccessToken(refreshToken, dispatch).then(restart);
}
return dispatch(logout()).then(() => Promise.reject(resp));
return dispatch(logoutAll()).then(() => Promise.reject(resp));
}
return Promise.reject(resp);
@ -76,7 +79,7 @@ function requestAccessToken(refreshToken, dispatch) {
return promise
.then(({token}) => dispatch(updateToken(token)))
.catch(() => dispatch(logout()));
.catch(() => dispatch(logoutAll()));
}

View File

@ -1,6 +1,8 @@
import expect from 'unexpected';
import sinon from 'sinon';
import { routeActions } from 'react-router-redux';
import accounts from 'services/api/accounts';
import authentication from 'services/api/authentication';
import {
@ -15,7 +17,7 @@ import {
} from 'components/accounts/actions';
import { SET_LOCALE } from 'components/i18n/actions';
import { updateUser } from 'components/user/actions';
import { updateUser, setUser } from 'components/user/actions';
const account = {
id: 1,
@ -257,6 +259,25 @@ describe('components/accounts/actions', () => {
reset()
]);
});
it('should redirect to /login', () =>
logoutAll()(dispatch, getState).then(() => {
expect(dispatch, 'to have a call satisfying', [
routeActions.push('/login')
]);
})
);
it('should change user to guest', () =>
logoutAll()(dispatch, getState).then(() => {
expect(dispatch, 'to have a call satisfying', [
setUser({
lang: user.lang,
isGuest: true
})
]);
})
);
});
describe('#logoutStrangers', () => {

View File

@ -1,134 +1,2 @@
import expect from 'unexpected';
import sinon from 'sinon';
import { routeActions } from 'react-router-redux';
import request from 'services/request';
import { reset, RESET } from 'components/accounts/actions';
import {
logout,
setUser
} from 'components/user/actions';
describe('components/user/actions', () => {
const getState = sinon.stub().named('store.getState');
const dispatch = sinon.spy((arg) =>
typeof arg === 'function' ? arg(dispatch, getState) : arg
).named('store.dispatch');
const callThunk = function(fn, ...args) {
const thunk = fn(...args);
return thunk(dispatch, getState);
};
beforeEach(() => {
dispatch.reset();
getState.reset();
getState.returns({});
sinon.stub(request, 'get').named('request.get');
sinon.stub(request, 'post').named('request.post');
});
afterEach(() => {
request.get.restore();
request.post.restore();
});
describe('#logout()', () => {
beforeEach(() => {
request.post.returns(Promise.resolve());
});
describe('user with jwt', () => {
const token = 'iLoveRockNRoll';
beforeEach(() => {
getState.returns({
user: {
lang: 'foo'
},
accounts: {
active: {token},
available: [{token}]
}
});
});
it('should post to /api/authentication/logout with user jwt', () =>
callThunk(logout).then(() => {
expect(request.post, 'to have a call satisfying', [
'/api/authentication/logout', {}, {
token: expect.it('not to be empty')
}
]);
})
);
testChangedToGuest();
testAccountsReset();
testRedirectedToLogin();
});
describe('user without jwt', () => {
// (a guest with partially filled user's state)
// DEPRECATED
beforeEach(() => {
getState.returns({
user: {
lang: 'foo'
},
accounts: {
active: null,
available: []
}
});
});
it('should not post to /api/authentication/logout', () =>
callThunk(logout).then(() => {
expect(request.post, 'was not called');
})
);
testChangedToGuest();
testAccountsReset();
testRedirectedToLogin();
});
function testChangedToGuest() {
it('should change user to guest', () =>
callThunk(logout).then(() => {
expect(dispatch, 'to have a call satisfying', [
setUser({
lang: 'foo',
isGuest: true
})
]);
})
);
}
function testRedirectedToLogin() {
it('should redirect to /login', () =>
callThunk(logout).then(() => {
expect(dispatch, 'to have a call satisfying', [
routeActions.push('/login')
]);
})
);
}
function testAccountsReset() {
it(`should dispatch ${RESET}`, () =>
callThunk(logout).then(() => {
expect(dispatch, 'to have a call satisfying', [
reset()
]);
})
);
}
});
});