Last active
August 20, 2025 02:36
-
Star
(183)
You must be signed in to star a gist -
Fork
(32)
You must be signed in to fork a gist
-
-
Save Dr-Nikson/1eabfc6bcc132384368c to your computer and use it in GitHub Desktop.
Revisions
-
Dr-Nikson revised this gist
Jul 4, 2015 . 2 changed files with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,5 @@ import { isAuthorized } from '../stores/auth'; import { checkAccess } from './auth-helpers'; /** * Creates requireAccess function and binds it to redux. This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,7 +5,7 @@ import { createRoutes } from './routes'; import createRedux from './redux/createRedux'; import fetchComponentsData from './core/fetchComponentsData'; import renderTemplate from './core/renderTemplate'; import { NotAuthorizedException, AccessDeniedException } from './core/auth-helpers.js'; import bindCheckAuth from './core/bindCheckAuth'; -
Dr-Nikson revised this gist
Jul 4, 2015 . 6 changed files with 2 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ # This is an auth example (WIP) *react + redux + RR* It uses https://gist.github.com/iNikNik/3c1b870f63dc0de67c38 for stores and actions. **1) create redux** This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import 'babel/polyfill'; import React from 'react'; import { Router } from 'react-router'; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import _ from 'lodash'; import invariant from 'react/lib/invariant'; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import { createStore, getActionIds } from '../redux/helpers.js'; import { AuthActions } from '../actions/AuthActions'; import { userRoles, accessLevels } from '../access'; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import { isAuthorized } from '../stores/auth'; import { checkAccess } from './authAccessLevels'; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import React from 'react'; import { Router, Route, DefaultRoute, Redirect } from 'react-router'; // eslint-disable-line no-unused-vars -
Dr-Nikson revised this gist
Jul 4, 2015 . 7 changed files with 70 additions and 38 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ # This is an auth example *react + redux + RR* _WIP_ It uses https://gist.github.com/iNikNik/3c1b870f63dc0de67c38 for stores and actions. **1) create redux** This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,4 @@ import 'babel/polyfill'; import React from 'react'; import { Router } from 'react-router'; @@ -7,27 +8,19 @@ import { Provider } from 'redux/react'; import createRedux from './redux/createRedux'; import { createRoutes } from './routes'; import getInitialState from './core/getInitialState'; import bindCheckAuth from './core/bindCheckAuth'; function run() { const reactRoot = window.document.getElementById('app'); const state = getInitialState('#__INITIAL_STATE__'); const redux = createRedux(state); const requireAccess = bindCheckAuth(redux, (nextState, transition) => { transition.to('/login', { next: nextState.location.pathname }); }, (nextState, transition) => { transition.to('/403'); }); const routes = createRoutes(requireAccess); const history = new BrowserHistory(); @@ -37,6 +30,14 @@ function run() { {() => <Router history={history} children={routes}/> } </Provider> ), reactRoot); if (process.env.NODE_ENV !== 'production') { window.React = React; // enable debugger if (!reactRoot || !reactRoot.firstChild || !reactRoot.firstChild.attributes || !reactRoot.firstChild.attributes['data-react-checksum']) { console.error('Server-side React render was discarded. Make sure that your initial render does not contain any client-side code.'); } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,4 @@ import _ from 'lodash'; import invariant from 'react/lib/invariant'; @@ -10,12 +11,18 @@ export function accessEquals(requiredLevel, currentLevel) { return requiredLevel.bitMask === currentLevel.bitMask; } export class NotAuthorizedException { constructor(to = '/login') { this.redirectTo = to; } } export class AccessDeniedException { constructor(to = '/403') { this.redirectTo = to; } } /* Method to build a distinct bit mask for each role It starts off with "1" and shifts the bit to the left for each element in the This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,11 +1,13 @@ import { createStore, getActionIds } from '../redux/helpers.js'; import { AuthActions } from '../actions/AuthActions'; import { userRoles, accessLevels } from '../access'; const actions = getActionIds(AuthActions); const initialState = { accessLvl: userRoles.public }; export const auth = createStore(initialState, { [actions.authenticate.success]: (state, action) => { @@ -16,3 +18,7 @@ export const auth = createStore(initialState, { }, }); export function isAuthorized(state) { return state.accessLvl.bitMask !== accessLevels.anon.bitMask; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,12 +1,31 @@ import { isAuthorized } from '../stores/auth'; import { checkAccess } from './authAccessLevels'; /** * Creates requireAccess function and binds it to redux. * * @param redux Redux instance * @param {Function} notAuthorizedHandler called when access is denied and user is not authorized (eq 401 code) * @param {Function} accessDeniedHandler called when access is denied for current user (eq 403 code) * @returns {Function} Return function with signature requireAuth(accessLevel, [checkAccessHandler]). * checkAccessHandler is optional, by default checkAccessHandler = checkAccess (from access-helpers.js) */ export default function bindCheckAuth(redux, notAuthorizedHandler, accessDeniedHandler) { return (accessLevel, checkAccessHandler = checkAccess) => (nextState, transition) => { const state = redux.getState().auth; const currentAccessLvl = state.accessLvl; if (checkAccessHandler(accessLevel, currentAccessLvl)) { // Access granted return; } if (!isAuthorized(state)) { notAuthorizedHandler(nextState, transition); return; } accessDeniedHandler(nextState, transition); }; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,20 +1,23 @@ import React from 'react'; import { Router, Route, DefaultRoute, Redirect } from 'react-router'; // eslint-disable-line no-unused-vars import { accessEquals } from './core/authAccessLevels.js'; import { accessLevels } from './access'; import App from './containers/App'; import HomePage from './containers/HomePage'; import InfoPage from './containers/InfoPage'; import LoginPage from './containers/LoginPage'; import AccessDeniedPage from './containers/AccessDeniedPage'; export const createRoutes = (requireAccess) => { return ( <Route name="app" component={App}> <Route name="home" path="/" component={HomePage} onEnter={requireAccess(accessLevels.user)}/> <Route name="info" path="/info" component={InfoPage} onEnter={requireAccess(accessLevels.user)}/> <Route name="login" path="/login" component={LoginPage} onEnter={requireAccess(accessLevels.anon, accessEquals)}/> <Route name="access-denied" path="/403" component={AccessDeniedPage} onEnter={requireAccess(accessLevels.public)}/> </Route> ); }; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,7 +5,7 @@ import { createRoutes } from './routes'; import createRedux from './redux/createRedux'; import fetchComponentsData from './core/fetchComponentsData'; import renderTemplate from './core/renderTemplate'; import { NotAuthorizedException, AccessDeniedException } from './core/authAccessLevels.js'; import bindCheckAuth from './core/bindCheckAuth'; @@ -18,16 +18,9 @@ server.get('*', (req, res, next) => { const redux = createRedux(); const requireAccess = bindCheckAuth(redux, (nextState) => { throw new NotAuthorizedException('/login?next=' + nextState.location.pathname); }, (nextState) => { throw new AccessDeniedException('/403?next=' + nextState.location.pathname); }); const routes = createRoutes(requireAccess); const location = new Location(req.path, req.query); @@ -45,10 +38,13 @@ server.get('*', (req, res, next) => { }); } catch (err) { // refactoring needed if (err instanceof NotAuthorizedException) { res.set('Content-Type', 'text/html'); res.status(401).send('<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=' + err.redirectTo + '"></head></html>'); } else if (err instanceof AccessDeniedException) { res.set('Content-Type', 'text/html'); res.status(403).send('<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=' + err.redirectTo + '"></head></html>'); } else { res.status(500).send(err.stack); next(err); -
Dr-Nikson revised this gist
Jul 2, 2015 . 2 changed files with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -17,7 +17,7 @@ function run() { const requireAccess = bindCheckAuth(redux, (nextState, transition) => { /* Current version need this hack to avoid infinite /login redirect Need to split error states: 1) "user not authorized" => [401] => redirect to login 2) "access denied" => [403] => redirect to 403 page */ This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -20,7 +20,7 @@ server.get('*', (req, res, next) => { const requireAccess = bindCheckAuth(redux, (nextState) => { /* Current version need this hack to avoid infinite /login redirect Need to split error states: 1) "user not authorized" => [401] => redirect to login 2) "access denied" => [403] => redirect to 403 page */ -
Dr-Nikson revised this gist
Jul 2, 2015 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -23,5 +23,7 @@ const createRoutes = (requireAccess) => { }; ``` **4) run router** **5) ...** **6) profit!** -
Dr-Nikson revised this gist
Jul 2, 2015 . 1 changed file with 5 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,11 +7,11 @@ It uses https://gist.github.com/iNikNik/3c1b870f63dc0de67c38 for stores and acti ```javascript const redux = createRedux(state); ``` **2) get requireAccess func => bindCheckAuth to redux** ```javascript const requireAccess = bindCheckAuth(redux, accessErrorHandler) ``` **3) pass onEnter callback to route** ```javascript const createRoutes = (requireAccess) => { return ( @@ -22,6 +22,6 @@ const createRoutes = (requireAccess) => { ); }; ``` **4) run router** **5) ...** **6) profit!** -
Dr-Nikson revised this gist
Jul 2, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,7 +3,7 @@ It uses https://gist.github.com/iNikNik/3c1b870f63dc0de67c38 for stores and actions. **1) create redux** ```javascript const redux = createRedux(state); ``` -
Dr-Nikson revised this gist
Jul 2, 2015 . 1 changed file with 6 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,15 +3,15 @@ It uses https://gist.github.com/iNikNik/3c1b870f63dc0de67c38 for stores and actions. 1. create redux ```javascript const redux = createRedux(state); ``` 2. get requireAccess func => bindCheckAuth to redux ```javascript const requireAccess = bindCheckAuth(redux, accessErrorHandler) ``` 3. pass onEnter callback to route ```javascript const createRoutes = (requireAccess) => { return ( @@ -22,6 +22,6 @@ const createRoutes = (requireAccess) => { ); }; ``` 4. run router 5. ... 6. profit! -
Dr-Nikson revised this gist
Jul 2, 2015 . 2 changed files with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -29,7 +29,7 @@ function run() { next: nextState.location.pathname }); }); const routes = createRoutes(requireAccess); const history = new BrowserHistory(); React.render(( This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -29,7 +29,7 @@ server.get('*', (req, res, next) => { } throw new NeedRedirect('/login?next=' + nextState.location.pathname); }); const routes = createRoutes(requireAccess); const location = new Location(req.path, req.query); Router.run(routes, location, async (error, initialState) => { -
Dr-Nikson revised this gist
Jul 2, 2015 . 3 changed files with 31 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,27 @@ # This is an auth example *react + redux + RR* It uses https://gist.github.com/iNikNik/3c1b870f63dc0de67c38 for stores and actions. ** 1) create redux ** ```javascript const redux = createRedux(state); ``` ** 2) get requireAccess func => bindCheckAuth to redux ** ```javascript const requireAccess = bindCheckAuth(redux, accessErrorHandler) ``` ** 3) pass onEnter callback to route ** ```javascript const createRoutes = (requireAccess) => { return ( <Route name="app" component={App}> <Route name="home" path="/" components={HomePage} onEnter={requireAccess(accessLevels.user)}/> ... </Route> ); }; ``` ** 4) run router ** ** 5) ... ** ** 6) profit! ** This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -9,7 +9,7 @@ import HomePage from './containers/HomePage'; import InfoPage from './containers/InfoPage'; import LoginPage from './containers/LoginPage'; export const createRoutes = (requireAccess) => { return ( <Route name="app" component={App}> <Route name="home" path="/" components={HomePage} onEnter={requireAccess(accessLevels.user)}/> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,6 +18,12 @@ server.get('*', (req, res, next) => { const redux = createRedux(); const requireAccess = bindCheckAuth(redux, (nextState) => { /* Current version need this hack to avoid infinite /login redirect Need to fetch error states: 1) "user not authorized" => [401] => redirect to login 2) "access denied" => [403] => redirect to 403 page */ if (nextState.location.pathname === '/login') { return; } -
Dr-Nikson revised this gist
Jul 2, 2015 . 8 changed files with 0 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ # This is an auth example *react + redux + RR* This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import 'babel/polyfill'; import React from 'react'; import { Router } from 'react-router'; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import _ from 'lodash'; import invariant from 'react/lib/invariant'; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import { createStore, getActionIds } from '../redux/helpers.js'; import { AuthActions } from '../actions/AuthActions'; import { userRoles } from '../access'; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import { createActions, asyncAction } from '../redux/helpers.js'; import { userRoles } from '../access'; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import { checkAccess } from './core/auth-helpers.js'; export default function bindCheckAuth(redux, accessFailHandler) { This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import React from 'react'; import { Router, Route, DefaultRoute, Redirect } from 'react-router'; // eslint-disable-line no-unused-vars This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ // ... import { createRoutes } from './routes'; -
Dr-Nikson created this gist
Jul 2, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,20 @@ import { createActions, asyncAction } from '../redux/helpers.js'; import { userRoles } from '../access'; export const AuthActions = createActions({ @asyncAction() authenticate(login, pass) { // success authentication mock - just for example :) const promise = new Promise((resolve) => { setTimeout(() => { resolve({accessLvl: userRoles.user}); }, 1000); }) ; return promise; }, }); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ # This is an auth example *react + redux + RR* It uses https://gist.github.com/iNikNik/3c1b870f63dc0de67c38 for stores and actions. This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,31 @@ import { buildRoles, buildAccessLevels } from './core/auth-helpers.js'; /* List all the roles you wish to use in the app You have a max of 31 before the bit shift pushes the accompanying integer out of the memory footprint for an integer */ const roles = [ 'banned', 'public', 'user', 'admin' ]; /* Build out all the access levels you want referencing the roles listed above You can use the "*" symbol to represent access to all roles. The left-hand side specifies the name of the access level, and the right-hand side specifies what user roles have access to that access level. E.g. users with user role 'user' and 'admin' have access to the access level 'user'. */ const levels = { 'public': '*', 'anon': ['public'], 'user': ['user', 'admin'], 'admin': ['admin'] }; export const userRoles = buildRoles(roles); export const accessLevels = buildAccessLevels(levels, userRoles); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,54 @@ import 'babel/polyfill'; import React from 'react'; import { Router } from 'react-router'; import BrowserHistory from 'react-router/lib/BrowserHistory'; import { Provider } from 'redux/react'; import createRedux from './redux/createRedux'; import { createRoutes } from './routes'; import getInitialState from './core/getInitialState'; import bindCheckAuth from './core/auth-helpers.js'; function run() { const reactRoot = window.document.getElementById('app'); const state = getInitialState('#__INITIAL_STATE__'); const redux = createRedux(state); const requireAccess = bindCheckAuth(redux, (nextState, transition) => { /* Current version need this hack to avoid infinite /login redirect Need to fetch error states: 1) "user not authorized" => [401] => redirect to login 2) "access denied" => [403] => redirect to 403 page */ if (nextState.location.pathname === '/login') { return; } transition.to('/login', { next: nextState.location.pathname }); }); const routes = createRoutes(redux, requireAccess); const history = new BrowserHistory(); React.render(( <Provider redux={redux}> {() => <Router history={history} children={routes}/> } </Provider> ), reactRoot); } // Run the application when both DOM is ready // and page content is loaded Promise.all([ new Promise((resolve) => { if (window.addEventListener) { window.addEventListener('DOMContentLoaded', resolve); } else { window.attachEvent('onload', resolve); } }) ]).then(run); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,114 @@ import _ from 'lodash'; import invariant from 'react/lib/invariant'; export function checkAccess(requiredLevel, currentLevel) { return !!(requiredLevel.bitMask & currentLevel.bitMask); } export function accessEquals(requiredLevel, currentLevel) { return requiredLevel.bitMask === currentLevel.bitMask; } export class NeedRedirect { constructor(to = '/login') { this.redirectTo = to; } } /* Method to build a distinct bit mask for each role It starts off with "1" and shifts the bit to the left for each element in the roles array parameter */ export function buildRoles(roles) { let bitMask = '01'; invariant( roles.length <= 31, 'You have too many roles!' + 'Max=31 before the bit shift pushes the accompanying integer out of the memory footprint for an integer' ); // dbg const userRoles = _.reduce(roles, (result, role) => { const intCode = parseInt(bitMask, 2); result[role] = { bitMask: intCode, title: role }; bitMask = (intCode << 1 ).toString(2); return result; }, {}); return userRoles; } /* This method builds access level bit masks based on the accessLevelDeclaration parameter which must contain an array for each access level containing the allowed user roles. */ export function buildAccessLevels(accessLevelDeclarations, userRoles) { /* Zero step - transform { level1Name: level1, level2Name: level2 } object => [ { name: level1Name, level: level1 }, { name: level2Name, level: level2 } ] array */ const declarationsArr = _.map(accessLevelDeclarations, (level, name) => ({ name, level })); /* First step: filter access levels like: 'public': '*', That means every user role enabled, so bitMask => sum of all bit masks */ let accessLevels = _ .filter(declarationsArr, ({ level }) => typeof level === 'string') // eslint-disable-line no-shadow .reduce((result, { level, name }) => { // eslint-disable-line no-shadow invariant( level === '*', 'Access Control Error: Could not parse "' + level + '" as access definition for level "' + name + '"' ); const resultBitMask = _.reduce(userRoles, (result) => result + '1', ''); // eslint-disable-line no-shadow result[name] = { bitMask: parseInt(resultBitMask, 2) }; return result; }, {}) ; /* Second step: filter access levels like: 'user': ['user', 'admin'], That means we need to iterate on ['user', 'admin'] array and summ bit mask for 'user' and 'admin' */ accessLevels = _ .filter(declarationsArr, ({ level }) => typeof level !== 'string') // eslint-disable-line no-shadow .reduce((result, { level, name }) => { // eslint-disable-line no-shadow const levelName = name; const levelsArr = level; const resultBitMask = _.reduce(levelsArr, (resultBitMask, roleName) => { // eslint-disable-line no-shadow invariant( userRoles.hasOwnProperty(roleName) === true, 'Access Control Error: Could not find role "' + roleName + '" in registered roles while building access for "' + levelName + '"' ); return resultBitMask | userRoles[roleName].bitMask; }, 0); result[name] = { bitMask: resultBitMask }; return result; }, accessLevels) ; return accessLevels; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,19 @@ import { createStore, getActionIds } from '../redux/helpers.js'; import { AuthActions } from '../actions/AuthActions'; import { userRoles } from '../access'; const actions = getActionIds(AuthActions); const initialState = { accessLvl: userRoles.public }; export const auth = createStore(initialState, { [actions.authenticate.success]: (state, action) => { return { ...state, accessLvl: action.result.accessLvl }; }, }); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,13 @@ import { checkAccess } from './core/auth-helpers.js'; export default function bindCheckAuth(redux, accessFailHandler) { return (accessLevel, customCheckFun) => (nextState, transition) => { const currentAccessLvl = redux.getState().auth.accessLvl; if (customCheckFun && !customCheckFun(accessLevel, currentAccessLvl) || !checkAccess(accessLevel, currentAccessLvl)) { accessFailHandler(nextState, transition); } }; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,21 @@ import React from 'react'; import { Router, Route, DefaultRoute, Redirect } from 'react-router'; // eslint-disable-line no-unused-vars import { accessEquals } from './core/auth-helpers.js'; import { accessLevels } from './access'; import App from './containers/App'; import HomePage from './containers/HomePage'; import InfoPage from './containers/InfoPage'; import LoginPage from './containers/LoginPage'; export const createRoutes = (redux, requireAccess) => { return ( <Route name="app" component={App}> <Route name="home" path="/" components={HomePage} onEnter={requireAccess(accessLevels.user)}/> <Route name="info" path="/info" components={InfoPage} onEnter={requireAccess(accessLevels.user)}/> <Route name="login" path="/login" components={LoginPage} onEnter={requireAccess(accessLevels.anon, accessEquals)}/> </Route> ); }; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,52 @@ // ... import { createRoutes } from './routes'; import createRedux from './redux/createRedux'; import fetchComponentsData from './core/fetchComponentsData'; import renderTemplate from './core/renderTemplate'; import { NeedRedirect } from './core/auth-helpers.js'; import bindCheckAuth from './core/bindCheckAuth'; // ... // server configuration // ... server.get('*', (req, res, next) => { try { const redux = createRedux(); const requireAccess = bindCheckAuth(redux, (nextState) => { if (nextState.location.pathname === '/login') { return; } throw new NeedRedirect('/login?next=' + nextState.location.pathname); }); const routes = createRoutes(redux, requireAccess); const location = new Location(req.path, req.query); Router.run(routes, location, async (error, initialState) => { try { const state = await fetchComponentsData(initialState.components, redux); const html = renderTemplate(redux, state, initialState, location); res.send(html).end(); } catch (err) { res.status(500).send(err.stack); next(err); } }); } catch (err) { if (err instanceof NeedRedirect) { // Redirect res.set('Content-Type', 'text/html'); res.status(401).send('<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=' + err.redirectTo + '"></head></html>'); } else { res.status(500).send(err.stack); next(err); } } });