UNPKG

recoder-code

Version:

Complete AI-powered development platform with ML model training, plugin registry, real-time collaboration, monitoring, infrastructure automation, and enterprise deployment capabilities

240 lines (171 loc) 6.94 kB
var _ = require('lodash'); var util = require('./util'); var debug = require('debug')('access'); function getOrigin(agent){ return (agent.stream.isServer) ? 'server' : 'browser'; } var operations = [ 'Read', 'Create', 'Delete', 'Update' ]; // Possible options: // dontUseOldDocs: false - if true don't save unupdated docs for update action exports = module.exports = ShareDBAccess; function ShareDBAccess(backend, options){ if (!(this instanceof ShareDBAccess)) return new ShareDBAccess(backend, options); this.options = options || {}; this.allow = {}; this.deny = {}; backend.use('doc', this.docHandler.bind(this)); backend.use('apply', this.applyHandler.bind(this)); backend.use('commit', this.commitHandler.bind(this)); this.initBackend(backend); } ShareDBAccess.prototype.initBackend = function(backend){ var allow = this.allow; var deny = this.deny; function registerAllowHandler(op){ if (backend['allow' + op]) return; backend['allow' + op] = function (collection, fn) { if(collection.indexOf('*') > -1) { allow[op]['**'] = allow[op]['**'] || []; allow[op]['**'].push({fn: fn, pattern: collection}); } else { allow[op][collection] = allow[op][collection] || []; allow[op][collection].push(fn); } }; } function registerDenyHandler(op){ if (backend['deny' + op]) return; backend['deny' + op] = function (collection, fn) { if(collection.indexOf('*') > -1) { deny[op]['**'] = deny[op]['**'] || []; deny[op]['**'].push({fn: fn, pattern: collection}); } else { deny[op][collection] = deny[op][collection] || []; deny[op][collection].push(fn); } }; } // Export functions operations.forEach(function(op){ allow[op] = allow[op] || {}; deny[op] = deny[op] || {}; registerAllowHandler(op); registerDenyHandler(op); }); }; // ++++++++++++++++++++++++++++++++ UPDATE ++++++++++++++++++++++++++++++++++ ShareDBAccess.prototype.commitHandler = function (shareRequest, done){ // Only derby-app client-request and server // if we set up checkServerAccess flag in stream // // we can set it up in the express middleware // before derby-apps routing in express // and set it off after var stream = shareRequest.agent.stream || {}; if (stream.isServer && !stream.checkServerAccess) return done(); var opData = shareRequest.op; if (opData.create || opData.del) return done(); var session = shareRequest.agent.connectSession || {}; var collection = shareRequest.index || shareRequest.collection; var docId = shareRequest.id; var oldDoc = (shareRequest.originalSnapshot && shareRequest.originalSnapshot.data) || {}; var newDoc = shareRequest.snapshot.data; var ops = opData.op; var ok = this.check('Update', collection, [docId, oldDoc, newDoc, ops, session]); debug('update', ok, collection, docId, oldDoc, newDoc, ops, session); if (ok) return done(); return done('403: Permission denied (update), collection: ' + collection + ', docId: '+ docId); }; ShareDBAccess.prototype.applyHandler = function (shareRequest, done) { var stream = shareRequest.agent.stream || {}; if (stream.isServer && !stream.checkServerAccess) return done(); var opData = shareRequest.op; var session = shareRequest.agent.connectSession || {}; var collection = shareRequest.index || shareRequest.collection; var docId = shareRequest.id; var snapshot = shareRequest.snapshot; // Save userId for audit purpose opData.m = opData.m || {}; opData.m.uId = session.userId; // ++++++++++++++++++++++++++++++++ CREATE ++++++++++++++++++++++++++++++++++ if (opData.create){ var doc = opData.create.data; var ok = this.check('Create', collection, [docId, doc, session]); debug('create', ok, collection, docId, doc); if (ok) return done(); return done('403: Permission denied (create), collection: ' + collection + ', docId: '+ docId); } // ++++++++++++++++++++++++++++++++ DELETE ++++++++++++++++++++++++++++++++++ if (opData.del) { var doc = snapshot.data; var ok = this.check('Delete', collection, [docId, doc, session]); debug('delete', ok, collection, docId, doc); if (ok) return done(); return done('403: Permission denied (delete), collection: ' + collection + ', docId: '+ docId); } // For Update if (!this.options.dontUseOldDocs) { shareRequest.originalSnapshot = _.cloneDeep(snapshot); } return done(); }; ShareDBAccess.prototype.docHandler = function (shareRequest, done){ // ++++++++++++++++++++++++++++++++ READ ++++++++++++++++++++++++++++++++++ var stream = shareRequest.agent.stream || {}; if (stream.isServer && !stream.checkServerAccess) return done(); var collection = shareRequest.index || shareRequest.collection; var docId = shareRequest.id; var doc = (shareRequest.snapshot && shareRequest.snapshot.data) || {}; var agent = shareRequest.agent; var session = agent.connectSession || {}; var ok = this.check('Read', collection, [docId, doc, session]); debug('read', ok, collection, [docId, doc, session]); if (ok) return done(); done('403: Permission denied (read), collection: ' + collection + ', docId: '+ docId); }; ShareDBAccess.prototype.check = function (operation, collection, args){ var allow = this.allow; var deny = this.deny; // First, check pattern matching collections allow[operation]['**'] = allow[operation]['**'] || []; deny[operation]['**'] = deny[operation]['**'] || []; var allowPatterns = allow[operation]['**']; var denyPatterns = deny[operation]['**']; allow [operation][collection] = allow [operation][collection] || []; deny [operation][collection] = deny [operation][collection] || []; var allowValidators = allow [operation][collection]; var denyValidators = deny [operation][collection]; var isAllowed = false; for(var i = 0, len = allowPatterns.length; i < len; i++) { var pattern = allowPatterns[i].pattern; var regExp = util.patternToRegExp(pattern); if(regExp.test(collection)) isAllowed = apply(allowPatterns[i]); if (isAllowed) break; } for (var i = 0; !isAllowed && i < allowValidators.length; i++) { isAllowed = apply(allowValidators[i]); if (isAllowed) break; } var isDenied = false; for(var i = 0, len = denyPatterns.length; i < len; i++) { var pattern = denyPatterns[i].pattern; var regExp = util.patternToRegExp(pattern); if(regExp.test(collection)) isDenied = apply(denyPatterns[i]); if (isDenied) break; } for (var j = 0; !isDenied && j < denyValidators.length; j++) { isDenied = apply(denyValidators[j]); if (isDenied) break; } return isAllowed && !isDenied; function apply(validator) { if (_.isFunction(validator)) return validator.apply(this, args); return validator.fn.apply(this, args); } }; exports.lookup = util.lookup;