Skip to content

Commit

Permalink
Merge pull request #999 from the-good-boy/middlewares
Browse files Browse the repository at this point in the history
feat(Admin System): Add middleware for securing routes according to privs
  • Loading branch information
MonkeyDo committed Jul 4, 2023
2 parents 58bec18 + 5d567d5 commit e1ba269
Show file tree
Hide file tree
Showing 24 changed files with 519 additions and 70 deletions.
4 changes: 3 additions & 1 deletion src/client/components/pages/parts/privs-edit-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ class PrivsEditModal extends React.Component {
},
method: 'POST'
});

if (!response.ok) {
if (response.status === 403) {
throw new Error(response.statusText);
}
const {error} = await response.json();
throw new Error(error ?? response.statusText);
}
Expand Down
18 changes: 18 additions & 0 deletions src/common/helpers/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,24 @@ export class PermissionDeniedError extends PathError {
}
}

export class NotAuthorizedError extends PathError {
static get defaultMessage() {
return 'You do not have permission to access this route';
}

static get status() {
return status.FORBIDDEN;
}

static detailedMessage(req) {
return [
`You do not have permission to access the following path:
${req.originalUrl}`,
'Please make sure you have the privileges to access the route!'
];
}
}

function _logError(err) {
log.error(err);
}
Expand Down
20 changes: 20 additions & 0 deletions src/server/helpers/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,23 @@ export function isAuthenticatedForCollectionView(req, res, next) {
'You do not have permission to view this collection', req
);
}

export function isAuthorized(flag) {
return async (req, res, next) => {
try {
const {Editor} = req.app.locals.orm;
const editor = await Editor.query({where: {id: req.user.id}})
.fetch({require: true});
/* eslint-disable no-bitwise */
if (editor.get('privs') & flag) {
return next();
}
throw new error.NotAuthorizedError(
'You do not have the privilege to access this route', req
);
}
catch (err) {
return next(err);
}
};
}
8 changes: 6 additions & 2 deletions src/server/routes/adminPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

import * as auth from '../helpers/auth';
import * as commonUtils from '../../common/helpers/utils';
import * as handler from '../helpers/handler';
import * as propHelpers from '../../client/helpers/props';
Expand All @@ -25,19 +26,22 @@ import {snakeCase as _snakeCase, isNil} from 'lodash';
import {escapeProps, generateProps} from '../helpers/props';
import AdminPanelSearchPage from '../../client/components/pages/admin-panel-search';
import Layout from '../../client/containers/layout';
import {PrivilegeTypes} from '../../common/helpers/privileges-utils';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import express from 'express';
import target from '../templates/target';


const ADMIN = PrivilegeTypes.ADMIN_PRIV.value;

const router = express.Router();

/**
* Generates React markup for the search page that is rendered by the user's
* browser.
*/
router.get('/', async (req, res, next) => {
router.get('/', auth.isAuthenticated, auth.isAuthorized(ADMIN), async (req, res, next) => {
const {orm} = req.app.locals;
const query = req.query.q ?? '';
const type = 'editor';
Expand Down Expand Up @@ -90,7 +94,7 @@ router.get('/', async (req, res, next) => {
}
});

router.get('/search', (req, res) => {
router.get('/search', auth.isAuthenticated, auth.isAuthorized(ADMIN), (req, res) => {
const {orm} = req.app.locals;
const query = req.query.q;
const type = 'editor';
Expand Down
5 changes: 4 additions & 1 deletion src/server/routes/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import CollectionsPage from '../../client/components/pages/collections';
import EditorContainer from '../../client/containers/editor';
import EditorRevisionPage from '../../client/components/pages/editor-revision';
import Layout from '../../client/containers/layout';
import {PrivilegeTypes} from '../../common/helpers/privileges-utils';
import ProfileForm from '../../client/components/forms/profile';
import ProfileTab from '../../client/components/pages/parts/editor-profile';
import React from 'react';
Expand All @@ -42,6 +43,8 @@ import {getOrderedRevisionForEditorPage} from '../helpers/revisions';
import target from '../templates/target';


const ADMIN = PrivilegeTypes.ADMIN_PRIV.value;

const router = express.Router();

router.get('/edit', auth.isAuthenticated, async (req, res, next) => {
Expand Down Expand Up @@ -174,7 +177,7 @@ router.post('/edit/handler', auth.isAuthenticatedForHandler, (req, res) => {
handler.sendPromiseResult(res, runAsync(), search.indexEntity);
});

router.post('/privs/edit/handler', auth.isAuthenticatedForHandler, async (req, res, next) => {
router.post('/privs/edit/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ADMIN), async (req, res, next) => {
const {Editor} = req.app.locals.orm;
try {
const editor = await Editor
Expand Down
19 changes: 11 additions & 8 deletions src/server/routes/entity/author.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
} from '../../helpers/entityRouteUtils';

import {ConflictError} from '../../../common/helpers/error';
import {PrivilegeTypes} from '../../../common/helpers/privileges-utils';
import _ from 'lodash';
import {escapeProps} from '../../helpers/props';
import express from 'express';
Expand Down Expand Up @@ -86,6 +87,8 @@ const mergeHandler = makeEntityCreateOrEditHandler(
'author', transformNewForm, additionalAuthorProps, true
);

const ENTITY_EDITOR = PrivilegeTypes.ENTITY_EDITING_PRIV.value;

/** ****************************
*********** Routes ************
*******************************/
Expand All @@ -94,7 +97,7 @@ const router = express.Router();

// Creation
router.get(
'/create', auth.isAuthenticated, middleware.loadIdentifierTypes,
'/create', auth.isAuthenticated, auth.isAuthorized(ENTITY_EDITOR), middleware.loadIdentifierTypes,
middleware.loadGenders, middleware.loadLanguages,
middleware.loadAuthorTypes, middleware.loadRelationshipTypes,
async (req, res) => {
Expand Down Expand Up @@ -134,7 +137,7 @@ router.get(
);

router.post(
'/create', entityRoutes.displayPreview, auth.isAuthenticatedForHandler, middleware.loadIdentifierTypes,
'/create', entityRoutes.displayPreview, auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR), middleware.loadIdentifierTypes,
middleware.loadGenders, middleware.loadLanguages,
middleware.loadAuthorTypes, middleware.loadRelationshipTypes,
async (req, res) => {
Expand Down Expand Up @@ -173,7 +176,7 @@ router.post(
}
);

router.post('/create/handler', auth.isAuthenticatedForHandler,
router.post('/create/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
createOrEditHandler);

/* If the route specifies a BBID, make sure it does not redirect to another bbid then load the corresponding entity */
Expand Down Expand Up @@ -203,7 +206,7 @@ router.get('/:bbid', middleware.loadEntityRelationships, middleware.loadWikipedi
entityRoutes.displayEntity(req, res);
});

router.get('/:bbid/delete', auth.isAuthenticated, (req, res, next) => {
router.get('/:bbid/delete', auth.isAuthenticated, auth.isAuthorized(ENTITY_EDITOR), (req, res, next) => {
if (!res.locals.entity.dataId) {
return next(new ConflictError('This entity has already been deleted'));
}
Expand All @@ -212,7 +215,7 @@ router.get('/:bbid/delete', auth.isAuthenticated, (req, res, next) => {
});

router.post(
'/:bbid/delete/handler', auth.isAuthenticatedForHandler,
'/:bbid/delete/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
(req, res) => {
const {orm} = req.app.locals;
const {AuthorHeader, AuthorRevision} = orm;
Expand Down Expand Up @@ -320,7 +323,7 @@ export function authorToFormState(author) {


router.get(
'/:bbid/edit', auth.isAuthenticated, middleware.loadIdentifierTypes,
'/:bbid/edit', auth.isAuthenticated, auth.isAuthorized(ENTITY_EDITOR), middleware.loadIdentifierTypes,
middleware.loadGenders, middleware.loadLanguages,
middleware.loadAuthorTypes, middleware.loadEntityRelationships,
middleware.loadRelationshipTypes,
Expand All @@ -341,10 +344,10 @@ router.get(
);


router.post('/:bbid/edit/handler', auth.isAuthenticatedForHandler,
router.post('/:bbid/edit/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
createOrEditHandler);

router.post('/:bbid/merge/handler', auth.isAuthenticatedForHandler,
router.post('/:bbid/merge/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
mergeHandler);

export default router;
18 changes: 10 additions & 8 deletions src/server/routes/entity/edition-group.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
} from '../../helpers/entityRouteUtils';

import {ConflictError} from '../../../common/helpers/error';
import {PrivilegeTypes} from '../../../common/helpers/privileges-utils';
import _ from 'lodash';
import {escapeProps} from '../../helpers/props';
import express from 'express';
Expand Down Expand Up @@ -82,6 +83,7 @@ const mergeHandler = makeEntityCreateOrEditHandler(
'editionGroup', transformNewForm, 'typeId', true
);

const ENTITY_EDITOR = PrivilegeTypes.ENTITY_EDITING_PRIV.value;

/** ****************************
*********** Routes ************
Expand All @@ -91,7 +93,7 @@ const router = express.Router();

// Creation
router.get(
'/create', auth.isAuthenticated, middleware.loadIdentifierTypes,
'/create', auth.isAuthenticated, auth.isAuthorized(ENTITY_EDITOR), middleware.loadIdentifierTypes,
middleware.loadLanguages, middleware.loadEditionGroupTypes,
middleware.loadRelationshipTypes,
async (req, res) => {
Expand Down Expand Up @@ -130,7 +132,7 @@ router.get(


router.post(
'/create', entityRoutes.displayPreview, auth.isAuthenticatedForHandler, middleware.loadIdentifierTypes,
'/create', entityRoutes.displayPreview, auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR), middleware.loadIdentifierTypes,
middleware.loadLanguages, middleware.loadEditionGroupTypes,
middleware.loadRelationshipTypes, async (req, res) => {
const entity = await utils.parseInitialState(req, 'editionGroup');
Expand All @@ -155,7 +157,7 @@ router.post(
}
);

router.post('/create/handler', auth.isAuthenticatedForHandler,
router.post('/create/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
createOrEditHandler);

/* If the route specifies a BBID, make sure it does not redirect to another bbid then load the corresponding entity */
Expand Down Expand Up @@ -194,7 +196,7 @@ router.get('/:bbid', middleware.loadEntityRelationships, middleware.loadWikipedi
entityRoutes.displayEntity(req, res);
});

router.get('/:bbid/delete', auth.isAuthenticated, (req, res, next) => {
router.get('/:bbid/delete', auth.isAuthenticated, auth.isAuthorized(ENTITY_EDITOR), (req, res, next) => {
if (!res.locals.entity.dataId) {
return next(new ConflictError('This entity has already been deleted'));
}
Expand All @@ -203,7 +205,7 @@ router.get('/:bbid/delete', auth.isAuthenticated, (req, res, next) => {
});

router.post(
'/:bbid/delete/handler', auth.isAuthenticatedForHandler,
'/:bbid/delete/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
(req, res) => {
const {orm} = req.app.locals;
const {EditionGroupHeader, EditionGroupRevision} = orm;
Expand Down Expand Up @@ -324,7 +326,7 @@ export function editionGroupToFormState(editionGroup) {
}

router.get(
'/:bbid/edit', auth.isAuthenticated, middleware.loadIdentifierTypes,
'/:bbid/edit', auth.isAuthenticated, auth.isAuthorized(ENTITY_EDITOR), middleware.loadIdentifierTypes,
middleware.loadEditionGroupTypes, middleware.loadLanguages,
middleware.loadEntityRelationships, middleware.loadRelationshipTypes,
(req, res) => {
Expand All @@ -341,10 +343,10 @@ router.get(
}
);

router.post('/:bbid/edit/handler', auth.isAuthenticatedForHandler,
router.post('/:bbid/edit/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
createOrEditHandler);

router.post('/:bbid/merge/handler', auth.isAuthenticatedForHandler,
router.post('/:bbid/merge/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
mergeHandler);

export default router;
19 changes: 11 additions & 8 deletions src/server/routes/entity/edition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
} from '../../helpers/entityRouteUtils';

import {ConflictError} from '../../../common/helpers/error';
import {PrivilegeTypes} from '../../../common/helpers/privileges-utils';
import {RelationshipTypes} from '../../../client/entity-editor/relationship-editor/types';
import _ from 'lodash';
import {escapeProps} from '../../helpers/props';
Expand Down Expand Up @@ -132,6 +133,8 @@ const mergeHandler = makeEntityCreateOrEditHandler(
'edition', transformNewForm, additionalEditionProps, true
);

const ENTITY_EDITOR = PrivilegeTypes.ENTITY_EDITING_PRIV.value;

/** ****************************
*********** Routes *************
*******************************/
Expand All @@ -140,7 +143,7 @@ const router = express.Router();
// Creation

router.get(
'/create', auth.isAuthenticated, middleware.loadIdentifierTypes,
'/create', auth.isAuthenticated, auth.isAuthorized(ENTITY_EDITOR), middleware.loadIdentifierTypes,
middleware.loadEditionStatuses, middleware.loadEditionFormats,
middleware.loadLanguages, middleware.loadRelationshipTypes,
(req:PassportRequest, res, next) => {
Expand Down Expand Up @@ -247,7 +250,7 @@ router.get(
);

router.post(
'/create', entityRoutes.displayPreview, auth.isAuthenticatedForHandler, middleware.loadIdentifierTypes,
'/create', entityRoutes.displayPreview, auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR), middleware.loadIdentifierTypes,
middleware.loadEditionStatuses, middleware.loadEditionFormats,
middleware.loadLanguages, middleware.loadRelationshipTypes,
async (req:PassportRequest, res, next) => {
Expand Down Expand Up @@ -296,7 +299,7 @@ router.post(
}
);

router.post('/create/handler', auth.isAuthenticatedForHandler,
router.post('/create/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
createOrEditHandler);

/* If the route specifies a BBID, make sure it does not redirect to another bbid then load the corresponding entity */
Expand Down Expand Up @@ -349,7 +352,7 @@ router.get('/:bbid/revisions/revisions', (req:PassportRequest, res, next) => {
});


router.get('/:bbid/delete', auth.isAuthenticated, (req:PassportRequest, res, next) => {
router.get('/:bbid/delete', auth.isAuthenticated, auth.isAuthorized(ENTITY_EDITOR), (req:PassportRequest, res, next) => {
if (!res.locals.entity.dataId) {
return next(new ConflictError('This entity has already been deleted'));
}
Expand All @@ -358,7 +361,7 @@ router.get('/:bbid/delete', auth.isAuthenticated, (req:PassportRequest, res, nex
});

router.post(
'/:bbid/delete/handler', auth.isAuthenticatedForHandler,
'/:bbid/delete/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
(req:PassportRequest, res) => {
const {orm} = req.app.locals;
const {EditionHeader, EditionRevision} = orm;
Expand Down Expand Up @@ -498,7 +501,7 @@ export function editionToFormState(edition) {
}

router.get(
'/:bbid/edit', auth.isAuthenticated, middleware.loadIdentifierTypes,
'/:bbid/edit', auth.isAuthenticated, auth.isAuthorized(ENTITY_EDITOR), middleware.loadIdentifierTypes,
middleware.loadEditionStatuses, middleware.loadEditionFormats,
middleware.loadLanguages, middleware.loadEntityRelationships,
middleware.loadRelationshipTypes,
Expand All @@ -516,10 +519,10 @@ router.get(
}
);

router.post('/:bbid/edit/handler', auth.isAuthenticatedForHandler,
router.post('/:bbid/edit/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
createOrEditHandler);

router.post('/:bbid/merge/handler', auth.isAuthenticatedForHandler,
router.post('/:bbid/merge/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ENTITY_EDITOR),
mergeHandler);

export default router;

0 comments on commit e1ba269

Please sign in to comment.