Skip to content

Instantly share code, notes, and snippets.

@lkmill
Last active April 28, 2017 14:37
Show Gist options
  • Select an option

  • Save lkmill/13b0352815b88a22a1c98cb656b1e4c4 to your computer and use it in GitHub Desktop.

Select an option

Save lkmill/13b0352815b88a22a1c98cb656b1e4c4 to your computer and use it in GitHub Desktop.

Revisions

  1. lkmill revised this gist Apr 28, 2017. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions acl-parse-permissions.js
    Original file line number Diff line number Diff line change
    @@ -16,13 +16,15 @@ module.exports = function parsePermissions(permissions) {

    let integer = actions ? +actions : 0;

    if (!_.isNumber(integer) {
    if (!_.isNumber(integer)) {
    actions = actions.split(',')

    integer = actions.reduce((result, action) => {
    const index = CRUD.indexOf(action);

    if (index < 0) throw new Error(`Unkown action "${action}"`);
    if (index < 0) {
    throw new Error(`Unkown action "${action}"`);
    }

    return result & 2^index;
    }, 0);
  2. lkmill revised this gist Apr 28, 2017. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion acl-parse-permissions.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    'use strict';

    const _ = require('lodash');

    const CRUD = ['create', 'read', 'update', 'delete'].reverse();

    // should be in format `entity:number` or `entity:action` or `entity:action1,action2,action3`
    @@ -14,7 +16,7 @@ module.exports = function parsePermissions(permissions) {

    let integer = actions ? +actions : 0;

    if (_.isNumber(integer) {
    if (!_.isNumber(integer) {
    actions = actions.split(',')

    integer = actions.reduce((result, action) => {
  3. lkmill revised this gist Apr 28, 2017. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions acl-parse-permissions.js
    Original file line number Diff line number Diff line change
    @@ -12,9 +12,9 @@ module.exports = function parsePermissions(permissions) {
    return permissions.reduce((result, str) => {
    let [ entity, actions ] = str.split(':');

    let integer = +actions;
    let integer = actions ? +actions : 0;

    if (_.isNumber(intger) {
    if (_.isNumber(integer) {
    actions = actions.split(',')

    integer = actions.reduce((result, action) => {
  4. lkmill revised this gist Apr 28, 2017. 1 changed file with 11 additions and 5 deletions.
    16 changes: 11 additions & 5 deletions acl-parse-permissions.js
    Original file line number Diff line number Diff line change
    @@ -11,14 +11,20 @@ module.exports = function parsePermissions(permissions) {

    return permissions.reduce((result, str) => {
    let [ entity, actions ] = str.split(':');

    let integer = +actions;

    if (_.isNumber(intger) {
    actions = actions.split(',')

    const integer = +actions || actions.reduce((result, action) => {
    const index = CRUD.indexOf(action);
    integer = actions.reduce((result, action) => {
    const index = CRUD.indexOf(action);

    if (index < 0) throw new Error(`Unkown action "${action}"`);
    if (index < 0) throw new Error(`Unkown action "${action}"`);

    return result & 2^index;
    }, 0);
    return result & 2^index;
    }, 0);
    }

    if (integer > 0) {
    result[entity] = integer;
  5. lkmill revised this gist Apr 28, 2017. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions acl-parse-permissions.js
    Original file line number Diff line number Diff line change
    @@ -5,9 +5,9 @@ const CRUD = ['create', 'read', 'update', 'delete'].reverse();
    // should be in format `entity:number` or `entity:action` or `entity:action1,action2,action3`
    // a user that can only create has 1000 bits > 8, only delete 0001 > 1, everything 1111 > 15
    module.exports = function parsePermissions(permissions) {
    if (!permissions) return undefined;

    if (!Array.isArray(permissions)) permissions = [ permissions ];
    if (!Array.isArray(permissions)) {
    permissions = [ permissions ];
    }

    return permissions.reduce((result, str) => {
    let [ entity, actions ] = str.split(':');
  6. lkmill revised this gist Apr 28, 2017. 1 changed file with 12 additions and 6 deletions.
    18 changes: 12 additions & 6 deletions acl-compare.js
    Original file line number Diff line number Diff line change
    @@ -1,13 +1,19 @@
    'use strict';

    module.exports = (permissions, requiredPermissions) => {
    if (requiredPermissions) {
    if (!permissions) return false;
    const _ = require('lodash');

    module.exports = (permissions, requiredPermissions) => {
    if (!_.isPlainObject(requiredPermissions)) {
    throw new TypeError('`requiredPermissions` is not an object');
    } else if (!_.isPlainObject(permissions)) {
    throw new TypeError('`permissions` is not an object');
    } else {
    for (const key in requiredPermissions) {
    if (!permissions[key] || permissions[key] & requiredPermissions[key] !== requiredPermissions[key]) return false;
    if (permissions[key] == null || (permissions[key] & requiredPermissions[key] !== requiredPermissions[key])) {
    return false;
    }
    }
    }

    return true;
    return true;
    }
    };
  7. lkmill revised this gist Apr 28, 2017. 1 changed file with 13 additions and 0 deletions.
    13 changes: 13 additions & 0 deletions acl-compare.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    'use strict';

    module.exports = (permissions, requiredPermissions) => {
    if (requiredPermissions) {
    if (!permissions) return false;

    for (const key in requiredPermissions) {
    if (!permissions[key] || permissions[key] & requiredPermissions[key] !== requiredPermissions[key]) return false;
    }
    }

    return true;
    };
  8. lkmill revised this gist Apr 28, 2017. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions acl-parse-permissions.js
    Original file line number Diff line number Diff line change
    @@ -3,6 +3,7 @@
    const CRUD = ['create', 'read', 'update', 'delete'].reverse();

    // should be in format `entity:number` or `entity:action` or `entity:action1,action2,action3`
    // a user that can only create has 1000 bits > 8, only delete 0001 > 1, everything 1111 > 15
    module.exports = function parsePermissions(permissions) {
    if (!permissions) return undefined;

  9. lkmill revised this gist Apr 28, 2017. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion acl-parse-permissions.js
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,7 @@

    const CRUD = ['create', 'read', 'update', 'delete'].reverse();

    // should be in format `entity:number` or `entity:action` or `entity:action1,action2,action3`
    module.exports = function parsePermissions(permissions) {
    if (!permissions) return undefined;

    @@ -10,7 +11,7 @@ module.exports = function parsePermissions(permissions) {
    return permissions.reduce((result, str) => {
    let [ entity, actions ] = str.split(':');

    const integer = actions.reduce((result, action) => {
    const integer = +actions || actions.reduce((result, action) => {
    const index = CRUD.indexOf(action);

    if (index < 0) throw new Error(`Unkown action "${action}"`);
  10. lkmill revised this gist Apr 28, 2017. 4 changed files with 137 additions and 33 deletions.
    27 changes: 27 additions & 0 deletions acl-parse-permissions.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    'use strict';

    const CRUD = ['create', 'read', 'update', 'delete'].reverse();

    module.exports = function parsePermissions(permissions) {
    if (!permissions) return undefined;

    if (!Array.isArray(permissions)) permissions = [ permissions ];

    return permissions.reduce((result, str) => {
    let [ entity, actions ] = str.split(':');

    const integer = actions.reduce((result, action) => {
    const index = CRUD.indexOf(action);

    if (index < 0) throw new Error(`Unkown action "${action}"`);

    return result & 2^index;
    }, 0);

    if (integer > 0) {
    result[entity] = integer;
    }

    return result;
    }, {});
    };
    50 changes: 50 additions & 0 deletions acl-request.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,50 @@
    'use strict';

    const _ = require('lodash');
    const compare = require('./compare');

    const parsePermissions = require('./util/parse-permissions');

    module.exports = (getPermissions, getRequiredPermissions) => {
    if (!_.isFunction(getPermissions) || !_.isFunction(getRequiredPermissions)) {
    throw new TypeError('Both parameters need to be functions');
    }

    return {
    fromReq(req, res, next) {
    Promise.all([
    getPermissions(req),
    getRequiredPermissions(req),
    ]).then((result) => compare(...result);
    }).then((authorized) => {
    if (authorized) return next();

    throw new ForbiddenError();
    }).catch(next);
    },

    // calls next if user has at least one of the required permissions
    // `requiredPermissions`should be of format `entity:action` or `entity:action1,action2,action3`
    all(...requiredPermissions) {
    if (!requiredPermissions.length) return (req, res, next) => next();

    requiredPermissions = parsePermissions(requiredPermissions);

    return (req, res, next) => {
    getPermissions(req).then((permissions))
    .catch(next);
    }
    },

    // calls next if user has at least one of the required permissions
    // `requiredPermissions`should be of format `entity:action` or `entity:action1,action2,action3`
    any(...requiredPermissions) {
    requiredPermissions = parsePermissions(requiredPermissions);

    return (req, res, next) => {
    getPermissions(req).then((permissions))
    .catch(next);
    }
    },
    };
    };
    33 changes: 0 additions & 33 deletions request.js
    Original file line number Diff line number Diff line change
    @@ -1,33 +0,0 @@
    'use strict';

    const req = require('http').IncomingMessage.prototype;

    const config = require('./config')

    // option 1
    Object.defineProperty(req, 'user', {
    get() {
    if (this.__user) return Promise.resolve(this.__user);

    if (!req.session && req.session.userId) return Promise.resolve(null);

    return config.deserializeUser(req.session.userId).then((user) => {
    this.__user = user;

    return user;
    })
    },
    });

    // option 2
    req.getUser() {
    if (this.user) return Promise.resolve(this.user);

    if (!req.session && req.session.userId) return Promise.resolve(null);

    return serializeUser(this.session.userId).then((user) => {
    this.user = user;

    return user;
    });
    }
    60 changes: 60 additions & 0 deletions session-request.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,60 @@
    'use strict';

    const _ = require('lodash');
    const req = require('http').IncomingMessage.prototype;

    module.exports = ({ serialize, deserialize }) => {
    if (!_.isFunction(serialize) || !_.isFunction(deserialize)) {
    throw new TypeError('Both arguments need to be functions');
    }

    req.login = function (user) {
    if (!req.session) Promise.reject(new Error('Session middleware not in use'));

    return serialize(user).then((result) => {
    req.session.user = result;
    }).catch((err) => {
    delete req.sesson.user;

    throw err;
    });
    }

    req.logout = function () {
    delete req.session.user;
    delete req.user;
    }

    req.isAuthenticated = function () {
    return this.session && this.session.user;
    }

    req.isUnauthenticated = function () {
    return !this.isAuthenticated();
    }

    // option1
    req.getUser = function () {
    if (this.__user) return Promise.resolve(this.__user);

    if (!req.session || !req.session.user) return Promise.resolve(null);

    return deserialize(this.session.user).then((user) => {
    this.__user = user;

    return user;
    });
    }

    Object.defineProperty(req, 'user', {
    get() {
    if (this.__user) return Promise.resolve(this.__user);

    if (!req.session || !req.session.user) return Promise.resolve(null);

    return deserialize(this.session.user).then((user) => {
    this.__user = user;

    return user;
    });
    }
  11. lkmill created this gist Apr 27, 2017.
    33 changes: 33 additions & 0 deletions request.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,33 @@
    'use strict';

    const req = require('http').IncomingMessage.prototype;

    const config = require('./config')

    // option 1
    Object.defineProperty(req, 'user', {
    get() {
    if (this.__user) return Promise.resolve(this.__user);

    if (!req.session && req.session.userId) return Promise.resolve(null);

    return config.deserializeUser(req.session.userId).then((user) => {
    this.__user = user;

    return user;
    })
    },
    });

    // option 2
    req.getUser() {
    if (this.user) return Promise.resolve(this.user);

    if (!req.session && req.session.userId) return Promise.resolve(null);

    return serializeUser(this.session.userId).then((user) => {
    this.user = user;

    return user;
    });
    }