UNPKG

synctos

Version:

The Syncmaker. A tool to build comprehensive sync functions for Couchbase Sync Gateway.

147 lines (130 loc) 5.85 kB
function authorizationModule(utils) { // A document definition may define its authorizations (channels, roles or users) for each operation type (view, add, replace, delete or // write) as either a string or an array of strings. In either case, add them to the list if they are not already present. function appendToAuthorizationList(allAuthorizations, authorizationsToAdd) { if (!utils.isValueNullOrUndefined(authorizationsToAdd)) { if (Array.isArray(authorizationsToAdd)) { for (var i = 0; i < authorizationsToAdd.length; i++) { var authorization = authorizationsToAdd[i]; if (allAuthorizations.indexOf(authorization) < 0) { allAuthorizations.push(authorization); } } } else if (allAuthorizations.indexOf(authorizationsToAdd) < 0) { allAuthorizations.push(authorizationsToAdd); } } } // Retrieves a list of channels the document belongs to based on its specified type function getAllDocChannels(docDefinition) { var docChannelMap = utils.resolveDocumentConstraint(docDefinition.channels); var allChannels = [ ]; if (docChannelMap) { appendToAuthorizationList(allChannels, docChannelMap.view); appendToAuthorizationList(allChannels, docChannelMap.write); appendToAuthorizationList(allChannels, docChannelMap.add); appendToAuthorizationList(allChannels, docChannelMap.replace); appendToAuthorizationList(allChannels, docChannelMap.remove); } return allChannels; } // Retrieves a list of authorizations (e.g. channels, roles, users) for the current document write operation type (add, replace or remove) function getRequiredAuthorizations(doc, oldDoc, authorizationDefinition) { var authorizationMap = utils.resolveDocumentConstraint(authorizationDefinition); if (utils.isValueNullOrUndefined(authorizationMap)) { // This document type does not define any authorizations (channels, roles, users) at all return null; } var requiredAuthorizations = [ ]; var writeAuthorizationFound = false; if (authorizationMap.write) { writeAuthorizationFound = true; appendToAuthorizationList(requiredAuthorizations, authorizationMap.write); } if (doc._deleted) { if (authorizationMap.remove) { writeAuthorizationFound = true; appendToAuthorizationList(requiredAuthorizations, authorizationMap.remove); } } else if (!utils.isDocumentMissingOrDeleted(oldDoc) && authorizationMap.replace) { writeAuthorizationFound = true; appendToAuthorizationList(requiredAuthorizations, authorizationMap.replace); } else if (utils.isDocumentMissingOrDeleted(oldDoc) && authorizationMap.add) { writeAuthorizationFound = true; appendToAuthorizationList(requiredAuthorizations, authorizationMap.add); } if (writeAuthorizationFound) { return requiredAuthorizations; } else { // This document type does not define any authorizations (channels, roles, users) that apply to this particular write operation type return null; } } // Ensures the user is authorized to create/replace/delete this document function authorize(doc, oldDoc, docDefinition) { var authorizedChannels = getRequiredAuthorizations(doc, oldDoc, docDefinition.channels); var authorizedRoles = getRequiredAuthorizations(doc, oldDoc, docDefinition.authorizedRoles); var authorizedUsers = getRequiredAuthorizations(doc, oldDoc, docDefinition.authorizedUsers); var channelMatch = false; if (authorizedChannels) { try { requireAccess(authorizedChannels); channelMatch = true; } catch (ex) { // The user has none of the authorized channels if (!authorizedRoles && !authorizedUsers) { // ... and the document definition does not specify any authorized roles or users throw ex; } } } var roleMatch = false; if (authorizedRoles) { try { requireRole(authorizedRoles); roleMatch = true; } catch (ex) { // The user belongs to none of the authorized roles if (!authorizedChannels && !authorizedUsers) { // ... and the document definition does not specify any authorized channels or users throw ex; } } } var userMatch = false; if (authorizedUsers) { try { requireUser(authorizedUsers); userMatch = true; } catch (ex) { // The user does not match any of the authorized usernames if (!authorizedChannels && !authorizedRoles) { // ... and the document definition does not specify any authorized channels or roles throw ex; } } } if (!authorizedChannels && !authorizedRoles && !authorizedUsers) { // The document type does not define any channels, roles or users that apply to this particular write operation type, so fall back to // Sync Gateway's default behaviour for an empty channel list: 403 Forbidden for requests via the public API and either 200 OK or 201 // Created for requests via the admin API. That way, the admin API will always be able to create, replace or remove documents, // regardless of their authorized channels, roles or users, as intended. requireAccess([ ]); } else if (!channelMatch && !roleMatch && !userMatch) { // None of the authorization methods (e.g. channels, roles, users) succeeded var errorMessage = 'missing channel access'; var error = new Error(errorMessage); error.forbidden = errorMessage; throw error; } return { channels: authorizedChannels, roles: authorizedRoles, users: authorizedUsers }; } return { authorize: authorize, getAllDocChannels: getAllDocChannels }; }