add authentcation error component, return 403 error from server when trying to save a project where the user doesn't match the owner
This commit is contained in:
parent
b018aa645c
commit
65592cbf9e
9 changed files with 130 additions and 38 deletions
|
@ -108,3 +108,5 @@ export const RESET_PROJECT_SAVED_TIME = 'RESET_PROJECT_SAVED_TIME';
|
|||
export const SET_PREVIOUS_PATH = 'SET_PREVIOUS_PATH';
|
||||
export const OPEN_FORCE_AUTHENTICATION = 'OPEN_FORCE_AUTHENTICATION';
|
||||
export const CLOSE_FORCE_AUTHENTICATION = 'CLOSE_FORCE_AUTHENTICATION';
|
||||
export const SHOW_AUTHENTICATION_ERROR = 'SHOW_AUTHENTICATION_ERROR';
|
||||
export const HIDE_AUTHENTICATION_ERROR = 'HIDE_AUTHENTICATION_ERROR';
|
||||
|
|
|
@ -232,3 +232,15 @@ export function closeForceAuthentication() {
|
|||
type: ActionTypes.CLOSE_FORCE_AUTHENTICATION
|
||||
};
|
||||
}
|
||||
|
||||
export function showAuthenticationError() {
|
||||
return {
|
||||
type: ActionTypes.SHOW_AUTHENTICATION_ERROR
|
||||
};
|
||||
}
|
||||
|
||||
export function hideAuthenticationError() {
|
||||
return {
|
||||
type: ActionTypes.HIDE_AUTHENTICATION_ERROR
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,7 +2,12 @@ import * as ActionTypes from '../../../constants';
|
|||
import { browserHistory } from 'react-router';
|
||||
import axios from 'axios';
|
||||
import { showToast, setToastText } from './toast';
|
||||
import { setUnsavedChanges, justOpenedProject, resetJustOpenedProject, setProjectSavedTime, resetProjectSavedTime } from './ide';
|
||||
import { setUnsavedChanges,
|
||||
justOpenedProject,
|
||||
resetJustOpenedProject,
|
||||
setProjectSavedTime,
|
||||
resetProjectSavedTime,
|
||||
showAuthenticationError } from './ide';
|
||||
import moment from 'moment';
|
||||
|
||||
const ROOT_URL = location.href.indexOf('localhost') > 0 ? 'http://localhost:8000/api' : '/api';
|
||||
|
@ -67,10 +72,16 @@ export function saveProject(autosave = false) {
|
|||
}
|
||||
}
|
||||
})
|
||||
.catch((response) => dispatch({
|
||||
.catch((response) => {
|
||||
if (response.status === 403) {
|
||||
dispatch(showAuthenticationError());
|
||||
} else {
|
||||
dispatch({
|
||||
type: ActionTypes.PROJECT_SAVE_FAIL,
|
||||
error: response.data
|
||||
}));
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
axios.post(`${ROOT_URL}/projects`, formParams, { withCredentials: true })
|
||||
.then(response => {
|
||||
|
|
24
client/modules/IDE/components/AuthenticationError.jsx
Normal file
24
client/modules/IDE/components/AuthenticationError.jsx
Normal file
|
@ -0,0 +1,24 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
function AuthenticationError(props) {
|
||||
return (
|
||||
<section className="authentication-error" tabIndex="0">
|
||||
<header className="authentication-error__header">
|
||||
<h2 className="authentication-error__title">Error</h2>
|
||||
</header>
|
||||
<div className="authentication-error__copy">
|
||||
<p>
|
||||
It looks like you've been logged out. Please
|
||||
<Link to="/login" onClick={props.closeModal}>log in</Link>.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
AuthenticationError.propTypes = {
|
||||
closeModal: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default AuthenticationError;
|
|
@ -10,6 +10,7 @@ import NewFolderModal from '../components/NewFolderModal';
|
|||
import ShareModal from '../components/ShareModal';
|
||||
import KeyboardShortcutModal from '../components/KeyboardShortcutModal';
|
||||
import ForceAuthentication from '../components/ForceAuthentication';
|
||||
import AuthenticationError from '../components/AuthenticationError';
|
||||
import Nav from '../../../components/Nav';
|
||||
import Console from '../components/Console';
|
||||
import Toast from '../components/Toast';
|
||||
|
@ -446,6 +447,17 @@ class IDEView extends React.Component {
|
|||
);
|
||||
}
|
||||
})()}
|
||||
{(() => { // eslint-disable-line
|
||||
if (this.props.ide.authenticationError) {
|
||||
return (
|
||||
<Overlay>
|
||||
<AuthenticationError
|
||||
closeModal={this.props.hideAuthenticationError}
|
||||
/>
|
||||
</Overlay>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
</div>
|
||||
|
||||
);
|
||||
|
@ -488,7 +500,8 @@ IDEView.propTypes = {
|
|||
infiniteLoopMessage: PropTypes.string.isRequired,
|
||||
projectSavedTime: PropTypes.string.isRequired,
|
||||
previousPath: PropTypes.string.isRequired,
|
||||
forceAuthenticationVisible: PropTypes.bool.isRequired
|
||||
forceAuthenticationVisible: PropTypes.bool.isRequired,
|
||||
authenticationError: PropTypes.bool.isRequired
|
||||
}).isRequired,
|
||||
startSketch: PropTypes.func.isRequired,
|
||||
stopSketch: PropTypes.func.isRequired,
|
||||
|
@ -591,7 +604,8 @@ IDEView.propTypes = {
|
|||
closeForceAuthentication: PropTypes.func.isRequired,
|
||||
openForceAuthentication: PropTypes.func.isRequired,
|
||||
console: PropTypes.array.isRequired,
|
||||
clearConsole: PropTypes.func.isRequired
|
||||
clearConsole: PropTypes.func.isRequired,
|
||||
hideAuthenticationError: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
|
@ -20,6 +20,7 @@ const initialState = {
|
|||
projectSavedTime: '',
|
||||
previousPath: '/',
|
||||
forceAuthenticationVisible: false,
|
||||
authenticationError: false
|
||||
};
|
||||
|
||||
const ide = (state = initialState, action) => {
|
||||
|
@ -96,6 +97,10 @@ const ide = (state = initialState, action) => {
|
|||
return Object.assign({}, state, { forceAuthenticationVisible: true });
|
||||
case ActionTypes.CLOSE_FORCE_AUTHENTICATION:
|
||||
return Object.assign({}, state, { forceAuthenticationVisible: false });
|
||||
case ActionTypes.SHOW_AUTHENTICATION_ERROR:
|
||||
return Object.assign({}, state, { authenticationError: true });
|
||||
case ActionTypes.HIDE_AUTHENTICATION_ERROR:
|
||||
return Object.assign({}, state, { authenticationError: false });
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
13
client/styles/components/_authentication-error.scss
Normal file
13
client/styles/components/_authentication-error.scss
Normal file
|
@ -0,0 +1,13 @@
|
|||
.authentication-error {
|
||||
@extend %modal;
|
||||
}
|
||||
|
||||
.authentication-error__header {
|
||||
padding: #{20 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
.authentication-error__copy {
|
||||
padding: #{20 / $base-font-size}rem;
|
||||
padding-top: 0;
|
||||
padding-bottom: #{60 / $base-font-size}rem;
|
||||
}
|
|
@ -33,6 +33,7 @@
|
|||
@import 'components/force-authentication';
|
||||
@import 'components/form-container';
|
||||
@import 'components/uploader';
|
||||
@import 'components/authentication-error';
|
||||
|
||||
@import 'layout/ide';
|
||||
@import 'layout/fullscreen';
|
||||
|
|
|
@ -23,6 +23,10 @@ export function createProject(req, res) {
|
|||
}
|
||||
|
||||
export function updateProject(req, res) {
|
||||
Project.findById(req.params.project_id, (err, project) => {
|
||||
if (!req.user || !project.user.equals(req.user._id)) {
|
||||
return res.status(403).send({ success: false, message: 'Session does not match owner of project.'});
|
||||
}
|
||||
Project.findByIdAndUpdate(req.params.project_id,
|
||||
{
|
||||
$set: req.body
|
||||
|
@ -50,6 +54,7 @@ export function updateProject(req, res) {
|
|||
}
|
||||
return res.json(updatedProject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getProject(req, res) {
|
||||
|
@ -64,12 +69,17 @@ export function getProject(req, res) {
|
|||
}
|
||||
|
||||
export function deleteProject(req, res) {
|
||||
Project.findById(req.params.project_id, (err, project) => {
|
||||
if (!req.user || !project.user.equals(req.user._id)) {
|
||||
return res.status(403).json({ success: false, message: 'Session does not match owner of project.'});
|
||||
}
|
||||
Project.remove({ _id: req.params.project_id }, (err) => {
|
||||
if (err) {
|
||||
return res.status(404).send({ message: 'Project with that id does not exist' });
|
||||
}
|
||||
return res.json({ success: true });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getProjects(req, res) {
|
||||
|
|
Loading…
Reference in a new issue