Authenticated routes (#207)

* Redirects authenticated routes to /login when user not authenticated or loading

* Redirect to login when call to get user fails
This commit is contained in:
Mike Stone 2016-09-20 14:17:31 -04:00 committed by Mike Arpaia
parent d2f1e749ab
commit 2f5ee8607d
4 changed files with 80 additions and 9 deletions

View file

@ -1,12 +1,44 @@
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { isEqual } from 'lodash';
import { push } from 'react-router-redux';
import paths from '../../router/paths';
export class AuthenticatedRoutes extends Component {
static propTypes = {
children: PropTypes.element,
dispatch: PropTypes.func,
loading: PropTypes.bool.isRequired,
user: PropTypes.object,
};
componentWillMount () {
const { loading, user } = this.props;
const { redirectToLogin } = this;
if (!loading && !user) return redirectToLogin();
return false;
}
componentWillReceiveProps (nextProps) {
if (isEqual(this.props, nextProps)) return false;
const { loading, user } = nextProps;
const { redirectToLogin } = this;
if (!loading && !user) return redirectToLogin();
return false;
}
redirectToLogin = () => {
const { dispatch } = this.props;
const { LOGIN } = paths;
return dispatch(push(LOGIN));
}
render () {
const { children, user } = this.props;
@ -21,9 +53,9 @@ export class AuthenticatedRoutes extends Component {
}
const mapStateToProps = (state) => {
const { user } = state.auth;
const { loading, user } = state.auth;
return { user };
return { loading, user };
};
export default connect(mapStateToProps)(AuthenticatedRoutes);

View file

@ -6,16 +6,35 @@ import AuthenticatedRoutes from './index';
import helpers from '../../test/helpers';
describe('AuthenticatedRoutes - component', () => {
const redirectToLoginAction = {
type: '@@router/CALL_HISTORY_METHOD',
payload: {
method: 'push',
args: ['/login'],
},
};
const renderedText = 'This text was rendered';
const storeWithUser = {
auth: {
loading: false,
user: {
id: 1,
email: 'hi@thegnar.co',
},
},
};
const storeWithoutUser = { auth: {} };
const storeWithoutUser = {
auth: {
loading: false,
user: null,
},
};
const storeLoadingUser = {
auth: {
loading: true,
user: null,
},
};
it('renders if there is a user in state', () => {
const { reduxMockStore } = helpers;
@ -31,7 +50,7 @@ describe('AuthenticatedRoutes - component', () => {
expect(component.text()).toEqual(renderedText);
});
it('does not render without a user in state', () => {
it('redirects to login without a user', () => {
const { reduxMockStore } = helpers;
const mockStore = reduxMockStore(storeWithoutUser);
const component = mount(
@ -42,6 +61,22 @@ describe('AuthenticatedRoutes - component', () => {
</Provider>
);
expect(mockStore.getActions()).toInclude(redirectToLoginAction);
expect(component.html()).toNotExist();
});
it('does not redirect to login if the user is loading', () => {
const { reduxMockStore } = helpers;
const mockStore = reduxMockStore(storeLoadingUser);
const component = mount(
<Provider store={mockStore}>
<AuthenticatedRoutes>
<div>{renderedText}</div>
</AuthenticatedRoutes>
</Provider>
);
expect(mockStore.getActions()).toNotInclude(redirectToLoginAction);
expect(component.html()).toNotExist();
});
});

View file

@ -1,13 +1,15 @@
/* eslint-disable no-unused-vars */
import { push } from 'react-router-redux';
import kolide from '../../kolide';
import { LOGIN_SUCCESS, LOGOUT_SUCCESS } from '../nodes/auth/actions';
import { LOGIN_FAILURE, LOGIN_SUCCESS, LOGOUT_SUCCESS } from '../nodes/auth/actions';
import local from '../../utilities/local';
import paths from '../../router/paths';
const authMiddleware = store => next => action => {
if (action.type === LOGIN_SUCCESS) {
const { token } = action.payload.data;
const { type, payload } = action;
if (type === LOGIN_SUCCESS) {
const { token } = payload.data;
if (token) {
local.setItem('auth_token', token);
@ -15,7 +17,7 @@ const authMiddleware = store => next => action => {
}
}
if (action.type === LOGOUT_SUCCESS) {
if (type === LOGOUT_SUCCESS || type === LOGIN_FAILURE) {
const { LOGIN } = paths;
local.clear();

View file

@ -20,7 +20,6 @@ const routes = (
<Provider store={store}>
<Router history={history}>
<Route path="/" component={radium(App)}>
<IndexRoute component={radium(HomePage)} />
<Route component={radium(LoginRoutes)}>
<Route path="forgot_password" component={radium(ForgotPasswordPage)} />
<Route path="login" component={radium(LoginPage)} />
@ -30,6 +29,9 @@ const routes = (
<Route path="logout" component={radium(LogoutPage)} />
</Route>
</Route>
<Route component={AuthenticatedRoutes}>
<IndexRoute component={radium(HomePage)} />
</Route>
</Route>
</Router>
</Provider>