UI to prevent suspended users viewing the application

This commit is contained in:
Jori Lallo
2018-03-04 22:18:23 -08:00
parent 1c2b3e992e
commit 1a8ca20fce
8 changed files with 49 additions and 9 deletions

View File

@ -1,6 +1,6 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { Switch, Route, withRouter } from 'react-router-dom'; import { Switch, Route, Redirect, withRouter } from 'react-router-dom';
import type { Location } from 'react-router-dom'; import type { Location } from 'react-router-dom';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import styled from 'styled-components'; import styled from 'styled-components';
@ -17,6 +17,7 @@ import Sidebar from 'components/Sidebar';
import SettingsSidebar from 'components/Sidebar/Settings'; import SettingsSidebar from 'components/Sidebar/Settings';
import Modals from 'components/Modals'; import Modals from 'components/Modals';
import Toasts from 'components/Toasts'; import Toasts from 'components/Toasts';
import ErrorSuspended from 'scenes/ErrorSuspended';
import AuthStore from 'stores/AuthStore'; import AuthStore from 'stores/AuthStore';
import UiStore from 'stores/UiStore'; import UiStore from 'stores/UiStore';
@ -66,11 +67,17 @@ class Layout extends React.Component {
this.props.ui.setActiveModal('keyboard-shortcuts'); this.props.ui.setActiveModal('keyboard-shortcuts');
} }
renderSuspended() {
return <ErrorSuspended />;
}
render() { render() {
const { auth, ui } = this.props; const { auth, ui } = this.props;
const { user, team } = auth; const { user, team } = auth;
const showSidebar = auth.authenticated && user && team; const showSidebar = auth.authenticated && user && team;
if (auth.isSuspended) return this.renderSuspended();
return ( return (
<Container column auto> <Container column auto>
<Helmet> <Helmet>

View File

@ -0,0 +1,24 @@
// @flow
import React from 'react';
import CenteredContent from 'components/CenteredContent';
import PageTitle from 'components/PageTitle';
const ErrorSuspended = () => (
<CenteredContent>
<PageTitle title="Your account has been suspended" />
<h1>
<span role="img" aria-label="Warning sign">
</span>{' '}
Your account has been suspended
</h1>
<p>
Team admin has suspended your account. To re-activate your account, please
reach out to them directly.
</p>
</CenteredContent>
);
export default ErrorSuspended;

View File

@ -0,0 +1,3 @@
// @flow
import ErrorSuspended from './ErrorSuspended';
export default ErrorSuspended;

View File

@ -14,6 +14,7 @@ class AuthStore {
@observable token: ?string; @observable token: ?string;
@observable oauthState: string; @observable oauthState: string;
@observable isLoading: boolean = false; @observable isLoading: boolean = false;
@observable isSuspended: boolean = false;
/* Computed */ /* Computed */
@ -43,8 +44,9 @@ class AuthStore {
this.team = res.data.team; this.team = res.data.team;
}); });
} catch (err) { } catch (err) {
// Failure to update user info is a non-fatal error. if (err.data.error === 'user_suspended') {
console.error(err); this.isSuspended = true;
}
} }
}; };

View File

@ -75,7 +75,9 @@ export default function auth() {
} }
} }
if (user.isSuspended) throw new UserSuspendedError(); if (user.isSuspended) {
throw new UserSuspendedError();
}
ctx.state.token = token; ctx.state.token = token;
ctx.state.user = user; ctx.state.user = user;

View File

@ -22,7 +22,9 @@ export function AdminRequiredError(
export function UserSuspendedError( export function UserSuspendedError(
message: string = 'Your access has been suspended by the team admin' message: string = 'Your access has been suspended by the team admin'
) { ) {
return httpErrors(403, message, { id: 'user_suspended' }); return httpErrors(403, message, {
id: 'user_suspended',
});
} }
export function InvalidRequestError(message: string = 'Request invalid') { export function InvalidRequestError(message: string = 'Request invalid') {

View File

@ -2,6 +2,10 @@ module.exports = {
up: async (queryInterface, Sequelize) => { up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('users', 'suspendedById', { await queryInterface.addColumn('users', 'suspendedById', {
type: Sequelize.UUID, type: Sequelize.UUID,
allowNull: true,
references: {
model: 'users',
},
}); });
await queryInterface.addColumn('users', 'suspendedAt', { await queryInterface.addColumn('users', 'suspendedAt', {
type: Sequelize.DATE, type: Sequelize.DATE,

View File

@ -49,10 +49,6 @@ User.associate = models => {
User.hasMany(models.ApiKey, { as: 'apiKeys' }); User.hasMany(models.ApiKey, { as: 'apiKeys' });
User.hasMany(models.Document, { as: 'documents' }); User.hasMany(models.Document, { as: 'documents' });
User.hasMany(models.View, { as: 'views' }); User.hasMany(models.View, { as: 'views' });
User.belongsTo(models.User, {
as: 'suspendedBy',
foreignKey: 'suspendedById',
});
}; };
// Instance methods // Instance methods