diff --git a/src/accesscontrol.js b/src/accesscontrol.js index 46f092b9c..507569409 100644 --- a/src/accesscontrol.js +++ b/src/accesscontrol.js @@ -1,140 +1,43 @@ 'use strict'; exports = module.exports = { - SCOPE_APPS_READ: 'apps:read', - SCOPE_APPS_MANAGE: 'apps:manage', - SCOPE_APPSTORE: 'appstore', - SCOPE_CLIENTS: 'clients', - SCOPE_CLOUDRON: 'cloudron', - SCOPE_DOMAINS_READ: 'domains:read', - SCOPE_DOMAINS_MANAGE: 'domains:manage', - SCOPE_MAIL: 'mail', - SCOPE_PROFILE: 'profile', - SCOPE_SETTINGS: 'settings', - SCOPE_SUBSCRIPTION: 'subscription', - SCOPE_USERS_READ: 'users:read', - SCOPE_USERS_MANAGE: 'users:manage', - VALID_SCOPES: [ 'apps', 'appstore', 'clients', 'cloudron', 'domains', 'mail', 'profile', 'settings', 'subscription', 'users' ], // keep this sorted + ROLE_ADMIN: 'admin', + ROLE_USER: 'user', - SCOPE_ANY: '*', - - validateScopeString: validateScopeString, - hasScopes: hasScopes, - canonicalScopeString: canonicalScopeString, - intersectScopes: intersectScopes, - validateToken: validateToken, - scopesForUser: scopesForUser + verifyToken: verifyToken, + hasRole: hasRole }; var assert = require('assert'), BoxError = require('./boxerror.js'), - debug = require('debug')('box:accesscontrol'), tokendb = require('./tokendb.js'), - users = require('./users.js'), - _ = require('underscore'); + users = require('./users.js'); -// returns scopes that does not have wildcards and is sorted -function canonicalScopeString(scope) { - if (scope === exports.SCOPE_ANY) return exports.VALID_SCOPES.join(','); - - return scope.split(',').sort().join(','); -} - -function intersectScopes(allowedScopes, wantedScopes) { - assert(Array.isArray(allowedScopes), 'Expecting sorted array'); - assert(Array.isArray(wantedScopes), 'Expecting sorted array'); - - if (_.isEqual(allowedScopes, wantedScopes)) return allowedScopes; // quick path - - let wantedScopesMap = new Map(); - let results = []; - - // make a map of scope -> [ subscopes ] - for (let w of wantedScopes) { - let parts = w.split(':'); - let subscopes = wantedScopesMap.get(parts[0]) || new Set(); - subscopes.add(parts[1] || '*'); - wantedScopesMap.set(parts[0], subscopes); - } - - for (let a of allowedScopes) { - let parts = a.split(':'); - let as = parts[1] || '*'; - - let subscopes = wantedScopesMap.get(parts[0]); - if (!subscopes) continue; - - if (subscopes.has('*') || subscopes.has(as)) { - results.push(a); - } else if (as === '*') { - results = results.concat(Array.from(subscopes).map(function (ss) { return `${a}:${ss}`; })); - } - } - - return results; -} - -function validateScopeString(scope) { - assert.strictEqual(typeof scope, 'string'); - - if (scope === '') return new BoxError(BoxError.BAD_FIELD, 'Empty scope not allowed', { field: 'scope' }); - - // NOTE: this function intentionally does not allow '*'. This is only allowed in the db to allow - // us not write a migration script every time we add a new scope - var allValid = scope.split(',').every(function (s) { return exports.VALID_SCOPES.indexOf(s.split(':')[0]) !== -1; }); - if (!allValid) return new BoxError(BoxError.BAD_FIELD, 'Invalid scope. Available scopes are ' + exports.VALID_SCOPES.join(', '), { field: 'scope' }); - return null; -} - -// tests if all requiredScopes are attached to the request -function hasScopes(authorizedScopes, requiredScopes) { - assert(Array.isArray(authorizedScopes), 'Expecting array'); - assert(Array.isArray(requiredScopes), 'Expecting array'); - - if (authorizedScopes.indexOf(exports.SCOPE_ANY) !== -1) return null; - - for (var i = 0; i < requiredScopes.length; ++i) { - const scopeParts = requiredScopes[i].split(':'); - - // this allows apps:write if the token has a higher apps scope - if (authorizedScopes.indexOf(requiredScopes[i]) === -1 && authorizedScopes.indexOf(scopeParts[0]) === -1) { - debug('scope: missing scope "%s".', requiredScopes[i]); - return new BoxError(BoxError.NOT_FOUND, 'Missing required scope "' + requiredScopes[i] + '"'); - } - } - - return null; -} - -function scopesForUser(user, callback) { +function hasRole(user, requiredRole) { assert.strictEqual(typeof user, 'object'); - assert.strictEqual(typeof callback, 'function'); + assert.strictEqual(typeof requiredRole, 'string'); - if (user.admin) return callback(null, exports.VALID_SCOPES); + if (requiredRole === exports.ROLE_USER) return null; + if (requiredRole === exports.ROLE_ADMIN && user.admin) return null; - callback(null, [ 'profile', 'apps:read' ]); + return new BoxError(BoxError.ACCESS_DENIED, 'Not allowed'); } -function validateToken(accessToken, callback) { +function verifyToken(accessToken, callback) { assert.strictEqual(typeof accessToken, 'string'); assert.strictEqual(typeof callback, 'function'); tokendb.getByAccessToken(accessToken, function (error, token) { - if (error && error.reason === BoxError.NOT_FOUND) return callback(null, null /* user */, 'Invalid Token'); // will end up as a 401 + if (error && error.reason === BoxError.NOT_FOUND) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); if (error) return callback(error); users.get(token.identifier, function (error, user) { - if (error && error.reason === BoxError.NOT_FOUND) return callback(null, null /* user */, 'Invalid Token'); // will end up as a 401 + if (error && error.reason === BoxError.NOT_FOUND) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); if (error) return callback(error); - if (!user.active) return callback(null, null /* user */, 'Invalid Token'); // will end up as a 401 + if (!user.active) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); - scopesForUser(user, function (error, userScopes) { - if (error) return callback(error); - - const authorizedScopes = intersectScopes(userScopes, token.scope.split(',')); - callback(null, user, { authorizedScopes }); // ends up in req.authInfo - }); + callback(null, user); }); }); } diff --git a/src/routes/accesscontrol.js b/src/routes/accesscontrol.js index 1ba605eca..2eb4c10c4 100644 --- a/src/routes/accesscontrol.js +++ b/src/routes/accesscontrol.js @@ -4,7 +4,7 @@ exports = module.exports = { passwordAuth: passwordAuth, tokenAuth: tokenAuth, - scope: scope, + authorize: authorize, websocketAuth: websocketAuth }; @@ -95,53 +95,41 @@ function tokenAuth(req, res, next) { if (!token) return next(new HttpError(401, 'Unauthorized')); - accesscontrol.validateToken(token, function (error, user, info) { + accesscontrol.verifyToken(token, function (error, user) { + if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new HttpError(401, 'Unauthorized')); if (error) return next(new HttpError(500, error.message)); - if (!user) return next(new HttpError(401, 'Unauthorized')); req.user = user; - req.authInfo = info; next(); }); } -// The scope middleware provides an auth middleware for routes. -// -// It is used for API routes, which are authenticated using accesstokens. -// Those accesstokens carry OAuth scopes and the middleware takes the required -// scope as an argument and will verify the accesstoken against it. -// -// See server.js: -// var profileScope = routes.oauth2.scope('profile'); -// -function scope(requiredScope) { - assert.strictEqual(typeof requiredScope, 'string'); - - var requiredScopes = requiredScope.split(','); +function authorize(requiredRole) { + assert.strictEqual(typeof requiredRole, 'string'); return function (req, res, next) { - assert(req.authInfo && typeof req.authInfo === 'object'); + assert.strictEqual(req.user, 'object'); - var error = accesscontrol.hasScopes(req.authInfo.authorizedScopes, requiredScopes); + var error = accesscontrol.hasRole(req.user, requiredRole); if (error) return next(new HttpError(403, error.message)); next(); }; } -function websocketAuth(requiredScopes, req, res, next) { - assert(Array.isArray(requiredScopes)); +function websocketAuth(requiredRole, req, res, next) { + assert.strictEqual(typeof requiredRole, 'string'); if (typeof req.query.access_token !== 'string') return next(new HttpError(401, 'Unauthorized')); - accesscontrol.validateToken(req.query.access_token, function (error, user, info) { + accesscontrol.verifyToken(req.query.access_token, function (error, user) { + if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new HttpError(401, 'Unauthorized')); if (error) return next(new HttpError(500, error.message)); - if (!user) return next(new HttpError(401, 'Unauthorized')); req.user = user; - var e = accesscontrol.hasScopes(info.authorizedScopes, requiredScopes); + var e = accesscontrol.hasRole(req.user, requiredRole); if (e) return next(new HttpError(403, e.message)); next(); diff --git a/src/server.js b/src/server.js index 7bb56acc7..24d4f8208 100644 --- a/src/server.js +++ b/src/server.js @@ -76,24 +76,11 @@ function initializeExpressSync() { var multipart = middleware.multipart({ maxFieldsSize: FIELD_LIMIT, limit: FILE_SIZE_LIMIT, timeout: FILE_TIMEOUT }); + // to keep routes code short const password = routes.accesscontrol.passwordAuth; const token = routes.accesscontrol.tokenAuth; - - // scope middleware implicitly also adds bearer token verification - var cloudronScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_CLOUDRON) ]; - var subscriptionScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_SUBSCRIPTION) ]; - var appstoreScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_APPSTORE) ]; - var profileScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_PROFILE) ]; - var usersReadScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_USERS_READ) ]; - var usersManageScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_USERS_MANAGE) ]; - var appsReadScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_APPS_READ) ]; - var appsManageScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_APPS_MANAGE) ]; - var settingsScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_SETTINGS) ]; - var mailScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_MAIL) ]; - var notificationsScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_PROFILE), routes.notifications.verifyOwnership ]; - var clientsScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_CLIENTS) ]; - var domainsReadScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_DOMAINS_READ) ]; - var domainsManageScope = [ token, routes.accesscontrol.scope(accesscontrol.SCOPE_DOMAINS_MANAGE) ]; + const authorizeAdmin = routes.accesscontrol.authorize(accesscontrol.ROLE_ADMIN); + const authorizeUser = routes.accesscontrol.authorize(accesscontrol.ROLE_USER); const verifyDomainLock = routes.domains.verifyDomainLock; @@ -116,191 +103,191 @@ function initializeExpressSync() { router.post('/api/v1/developer/login', password, routes.cloudron.login); // DEPRECATED we should use the regular /api/v1/cloudron/login // cloudron routes - router.get ('/api/v1/cloudron/update', cloudronScope, routes.cloudron.getUpdateInfo); - router.post('/api/v1/cloudron/update', cloudronScope, routes.cloudron.update); - router.post('/api/v1/cloudron/prepare_dashboard_domain', cloudronScope, routes.cloudron.prepareDashboardDomain); - router.post('/api/v1/cloudron/set_dashboard_domain', cloudronScope, routes.cloudron.setDashboardAndMailDomain); - router.post('/api/v1/cloudron/renew_certs', cloudronScope, routes.cloudron.renewCerts); - router.post('/api/v1/cloudron/check_for_updates', cloudronScope, routes.cloudron.checkForUpdates); - router.get ('/api/v1/cloudron/reboot', cloudronScope, routes.cloudron.isRebootRequired); - router.post('/api/v1/cloudron/reboot', cloudronScope, routes.cloudron.reboot); - router.get ('/api/v1/cloudron/graphs', cloudronScope, routes.graphs.getGraphs); - router.get ('/api/v1/cloudron/disks', cloudronScope, routes.cloudron.getDisks); - router.get ('/api/v1/cloudron/memory', cloudronScope, routes.cloudron.getMemory); - router.get ('/api/v1/cloudron/logs/:unit', cloudronScope, routes.cloudron.getLogs); - router.get ('/api/v1/cloudron/logstream/:unit', cloudronScope, routes.cloudron.getLogStream); - router.get ('/api/v1/cloudron/eventlog', cloudronScope, routes.eventlog.list); - router.get ('/api/v1/cloudron/eventlog/:eventId', cloudronScope, routes.eventlog.get); - router.post('/api/v1/cloudron/sync_external_ldap', cloudronScope, routes.cloudron.syncExternalLdap); - router.get ('/api/v1/cloudron/server_ip', cloudronScope, routes.cloudron.getServerIp); + router.get ('/api/v1/cloudron/update', token, authorizeAdmin, routes.cloudron.getUpdateInfo); + router.post('/api/v1/cloudron/update', token, authorizeAdmin, routes.cloudron.update); + router.post('/api/v1/cloudron/prepare_dashboard_domain', token, authorizeAdmin, routes.cloudron.prepareDashboardDomain); + router.post('/api/v1/cloudron/set_dashboard_domain', token, authorizeAdmin, routes.cloudron.setDashboardAndMailDomain); + router.post('/api/v1/cloudron/renew_certs', token, authorizeAdmin, routes.cloudron.renewCerts); + router.post('/api/v1/cloudron/check_for_updates', token, authorizeAdmin, routes.cloudron.checkForUpdates); + router.get ('/api/v1/cloudron/reboot', token, authorizeAdmin, routes.cloudron.isRebootRequired); + router.post('/api/v1/cloudron/reboot', token, authorizeAdmin, routes.cloudron.reboot); + router.get ('/api/v1/cloudron/graphs', token, authorizeAdmin, routes.graphs.getGraphs); + router.get ('/api/v1/cloudron/disks', token, authorizeAdmin, routes.cloudron.getDisks); + router.get ('/api/v1/cloudron/memory', token, authorizeAdmin, routes.cloudron.getMemory); + router.get ('/api/v1/cloudron/logs/:unit', token, authorizeAdmin, routes.cloudron.getLogs); + router.get ('/api/v1/cloudron/logstream/:unit', token, authorizeAdmin, routes.cloudron.getLogStream); + router.get ('/api/v1/cloudron/eventlog', token, authorizeAdmin, routes.eventlog.list); + router.get ('/api/v1/cloudron/eventlog/:eventId', token, authorizeAdmin, routes.eventlog.get); + router.post('/api/v1/cloudron/sync_external_ldap', token, authorizeAdmin, routes.cloudron.syncExternalLdap); + router.get ('/api/v1/cloudron/server_ip', token, authorizeAdmin, routes.cloudron.getServerIp); // tasks - router.get ('/api/v1/tasks', settingsScope, routes.tasks.list); - router.get ('/api/v1/tasks/:taskId', settingsScope, routes.tasks.get); - router.get ('/api/v1/tasks/:taskId/logs', cloudronScope, routes.tasks.getLogs); - router.get ('/api/v1/tasks/:taskId/logstream', cloudronScope, routes.tasks.getLogStream); - router.post('/api/v1/tasks/:taskId/stop', settingsScope, routes.tasks.stopTask); + router.get ('/api/v1/tasks', token, authorizeAdmin, routes.tasks.list); + router.get ('/api/v1/tasks/:taskId', token, authorizeAdmin, routes.tasks.get); + router.get ('/api/v1/tasks/:taskId/logs', token, authorizeAdmin, routes.tasks.getLogs); + router.get ('/api/v1/tasks/:taskId/logstream', token, authorizeAdmin, routes.tasks.getLogStream); + router.post('/api/v1/tasks/:taskId/stop', token, authorizeAdmin, routes.tasks.stopTask); // notifications - router.get ('/api/v1/notifications', notificationsScope, routes.notifications.list); - router.get ('/api/v1/notifications/:notificationId', notificationsScope, routes.notifications.get); - router.post('/api/v1/notifications/:notificationId', notificationsScope, routes.notifications.ack); + router.get ('/api/v1/notifications', token, authorizeUser, routes.notifications.verifyOwnership, routes.notifications.list); + router.get ('/api/v1/notifications/:notificationId', token, authorizeUser, routes.notifications.verifyOwnership, routes.notifications.get); + router.post('/api/v1/notifications/:notificationId', token, authorizeUser, routes.notifications.verifyOwnership, routes.notifications.ack); // backups - router.get ('/api/v1/backups', settingsScope, routes.backups.list); - router.post('/api/v1/backups/create', settingsScope, routes.backups.startBackup); - router.post('/api/v1/backups/cleanup', settingsScope, routes.backups.cleanup); + router.get ('/api/v1/backups', token, authorizeAdmin, routes.backups.list); + router.post('/api/v1/backups/create', token, authorizeAdmin, routes.backups.startBackup); + router.post('/api/v1/backups/cleanup', token, authorizeAdmin, routes.backups.cleanup); // config route (for dashboard) - router.get ('/api/v1/config', profileScope, routes.cloudron.getConfig); + router.get ('/api/v1/config', token, authorizeUser, routes.cloudron.getConfig); // working off the user behind the provided token - router.get ('/api/v1/profile', profileScope, routes.profile.get); - router.post('/api/v1/profile', profileScope, routes.profile.update); + router.get ('/api/v1/profile', token, authorizeUser, routes.profile.get); + router.post('/api/v1/profile', token, authorizeUser, routes.profile.update); router.get ('/api/v1/profile/avatar/:identifier', routes.profile.getAvatar); // this is not scoped so it can used directly in img tag - router.post('/api/v1/profile/avatar', profileScope, multipart, routes.profile.setAvatar); - router.del ('/api/v1/profile/avatar', profileScope, routes.profile.clearAvatar); - router.post('/api/v1/profile/password', profileScope, routes.users.verifyPassword, routes.profile.changePassword); - router.post('/api/v1/profile/twofactorauthentication', profileScope, routes.profile.setTwoFactorAuthenticationSecret); - router.post('/api/v1/profile/twofactorauthentication/enable', profileScope, routes.profile.enableTwoFactorAuthentication); - router.post('/api/v1/profile/twofactorauthentication/disable', profileScope, routes.users.verifyPassword, routes.profile.disableTwoFactorAuthentication); + router.post('/api/v1/profile/avatar', token, authorizeUser, multipart, routes.profile.setAvatar); + router.del ('/api/v1/profile/avatar', token, authorizeUser, routes.profile.clearAvatar); + router.post('/api/v1/profile/password', token, authorizeUser, routes.users.verifyPassword, routes.profile.changePassword); + router.post('/api/v1/profile/twofactorauthentication', token, authorizeUser, routes.profile.setTwoFactorAuthenticationSecret); + router.post('/api/v1/profile/twofactorauthentication/enable', token, authorizeUser, routes.profile.enableTwoFactorAuthentication); + router.post('/api/v1/profile/twofactorauthentication/disable', token, authorizeUser, routes.users.verifyPassword, routes.profile.disableTwoFactorAuthentication); - router.get ('/api/v1/app_passwords', profileScope, routes.appPasswords.list); - router.post('/api/v1/app_passwords', profileScope, routes.appPasswords.add); - router.get ('/api/v1/app_passwords/:id', profileScope, routes.appPasswords.get); - router.del ('/api/v1/app_passwords/:id', profileScope, routes.appPasswords.del); + router.get ('/api/v1/app_passwords', token, authorizeUser, routes.appPasswords.list); + router.post('/api/v1/app_passwords', token, authorizeUser, routes.appPasswords.add); + router.get ('/api/v1/app_passwords/:id', token, authorizeUser, routes.appPasswords.get); + router.del ('/api/v1/app_passwords/:id', token, authorizeUser, routes.appPasswords.del); // user routes - router.get ('/api/v1/users', usersReadScope, routes.users.list); - router.post('/api/v1/users', usersManageScope, routes.users.create); - router.get ('/api/v1/users/:userId', usersManageScope, routes.users.get); // this is manage scope because it returns non-restricted fields - router.del ('/api/v1/users/:userId', usersManageScope, routes.users.remove); - router.post('/api/v1/users/:userId', usersManageScope, routes.users.update); - router.post('/api/v1/users/:userId/password', usersManageScope, routes.users.changePassword); - router.put ('/api/v1/users/:userId/groups', usersManageScope, routes.users.setGroups); - router.post('/api/v1/users/:userId/send_invite', usersManageScope, routes.users.sendInvite); - router.post('/api/v1/users/:userId/create_invite', usersManageScope, routes.users.createInvite); + router.get ('/api/v1/users', token, authorizeUser, routes.users.list); + router.post('/api/v1/users', token, authorizeAdmin, routes.users.create); + router.get ('/api/v1/users/:userId', token, authorizeAdmin, routes.users.get); // this is manage scope because it returns non-restricted fields + router.del ('/api/v1/users/:userId', token, authorizeAdmin, routes.users.remove); + router.post('/api/v1/users/:userId', token, authorizeAdmin, routes.users.update); + router.post('/api/v1/users/:userId/password', token, authorizeAdmin, routes.users.changePassword); + router.put ('/api/v1/users/:userId/groups', token, authorizeAdmin, routes.users.setGroups); + router.post('/api/v1/users/:userId/send_invite', token, authorizeAdmin, routes.users.sendInvite); + router.post('/api/v1/users/:userId/create_invite', token, authorizeAdmin, routes.users.createInvite); // Group management - router.get ('/api/v1/groups', usersReadScope, routes.groups.list); - router.post('/api/v1/groups', usersManageScope, routes.groups.create); - router.get ('/api/v1/groups/:groupId', usersManageScope, routes.groups.get); - router.put ('/api/v1/groups/:groupId/members', usersManageScope, routes.groups.updateMembers); - router.post('/api/v1/groups/:groupId', usersManageScope, routes.groups.update); - router.del ('/api/v1/groups/:groupId', usersManageScope, routes.groups.remove); + router.get ('/api/v1/groups', token, authorizeUser, routes.groups.list); + router.post('/api/v1/groups', token, authorizeAdmin, routes.groups.create); + router.get ('/api/v1/groups/:groupId', token, authorizeAdmin, routes.groups.get); + router.put ('/api/v1/groups/:groupId/members', token, authorizeAdmin, routes.groups.updateMembers); + router.post('/api/v1/groups/:groupId', token, authorizeAdmin, routes.groups.update); + router.del ('/api/v1/groups/:groupId', token, authorizeAdmin, routes.groups.remove); // client/token routes - router.get ('/api/v1/clients', clientsScope, routes.clients.getAll); - router.post('/api/v1/clients', clientsScope, routes.clients.add); - router.get ('/api/v1/clients/:clientId', clientsScope, routes.clients.get); - router.post('/api/v1/clients/:clientId', clientsScope, routes.clients.add); - router.del ('/api/v1/clients/:clientId', clientsScope, routes.clients.del); - router.get ('/api/v1/clients/:clientId/tokens', clientsScope, routes.clients.getTokens); - router.post('/api/v1/clients/:clientId/tokens', clientsScope, routes.clients.addToken); - router.del ('/api/v1/clients/:clientId/tokens', clientsScope, routes.clients.delTokens); - router.del ('/api/v1/clients/:clientId/tokens/:tokenId', clientsScope, routes.clients.delToken); + router.get ('/api/v1/clients', token, authorizeAdmin, routes.clients.getAll); + router.post('/api/v1/clients', token, authorizeAdmin, routes.clients.add); + router.get ('/api/v1/clients/:clientId', token, authorizeAdmin, routes.clients.get); + router.post('/api/v1/clients/:clientId', token, authorizeAdmin, routes.clients.add); + router.del ('/api/v1/clients/:clientId', token, authorizeAdmin, routes.clients.del); + router.get ('/api/v1/clients/:clientId/tokens', token, authorizeAdmin, routes.clients.getTokens); + router.post('/api/v1/clients/:clientId/tokens', token, authorizeAdmin, routes.clients.addToken); + router.del ('/api/v1/clients/:clientId/tokens', token, authorizeAdmin, routes.clients.delTokens); + router.del ('/api/v1/clients/:clientId/tokens/:tokenId', token, authorizeAdmin, routes.clients.delToken); // appstore and subscription routes - router.post('/api/v1/appstore/register_cloudron', subscriptionScope, routes.appstore.registerCloudron); - router.get ('/api/v1/appstore/subscription', subscriptionScope, routes.appstore.getSubscription); - router.get ('/api/v1/appstore/apps', appstoreScope, routes.appstore.getApps); - router.get ('/api/v1/appstore/apps/:appstoreId', appstoreScope, routes.appstore.getApp); - router.get ('/api/v1/appstore/apps/:appstoreId/versions/:versionId', appstoreScope, routes.appstore.getAppVersion); + router.post('/api/v1/appstore/register_cloudron', token, authorizeAdmin, routes.appstore.registerCloudron); + router.get ('/api/v1/appstore/subscription', token, authorizeAdmin, routes.appstore.getSubscription); + router.get ('/api/v1/appstore/apps', token, authorizeAdmin, routes.appstore.getApps); + router.get ('/api/v1/appstore/apps/:appstoreId', token, authorizeAdmin, routes.appstore.getApp); + router.get ('/api/v1/appstore/apps/:appstoreId/versions/:versionId', token, authorizeAdmin, routes.appstore.getAppVersion); // app routes - router.get ('/api/v1/apps', appsReadScope, routes.apps.getApps); - router.get ('/api/v1/apps/:id', appsManageScope, routes.apps.getApp); - router.get ('/api/v1/apps/:id/icon', appsReadScope, routes.apps.getAppIcon); + router.get ('/api/v1/apps', token, authorizeUser, routes.apps.getApps); + router.get ('/api/v1/apps/:id', token, authorizeAdmin, routes.apps.getApp); + router.get ('/api/v1/apps/:id/icon', token, authorizeUser, routes.apps.getAppIcon); - router.post('/api/v1/apps/install', appsManageScope, routes.apps.installApp); - router.post('/api/v1/apps/:id/uninstall', appsManageScope, routes.apps.uninstallApp); + router.post('/api/v1/apps/install', token, authorizeAdmin, routes.apps.installApp); + router.post('/api/v1/apps/:id/uninstall', token, authorizeAdmin, routes.apps.uninstallApp); - router.post('/api/v1/apps/:id/configure/access_restriction', appsManageScope, routes.apps.setAccessRestriction); - router.post('/api/v1/apps/:id/configure/label', appsManageScope, routes.apps.setLabel); - router.post('/api/v1/apps/:id/configure/tags', appsManageScope, routes.apps.setTags); - router.post('/api/v1/apps/:id/configure/icon', appsManageScope, routes.apps.setIcon); - router.post('/api/v1/apps/:id/configure/memory_limit', appsManageScope, routes.apps.setMemoryLimit); - router.post('/api/v1/apps/:id/configure/cpu_shares', appsManageScope, routes.apps.setCpuShares); - router.post('/api/v1/apps/:id/configure/automatic_backup', appsManageScope, routes.apps.setAutomaticBackup); - router.post('/api/v1/apps/:id/configure/automatic_update', appsManageScope, routes.apps.setAutomaticUpdate); - router.post('/api/v1/apps/:id/configure/reverse_proxy', appsManageScope, routes.apps.setReverseProxyConfig); - router.post('/api/v1/apps/:id/configure/cert', appsManageScope, routes.apps.setCertificate); - router.post('/api/v1/apps/:id/configure/debug_mode', appsManageScope, routes.apps.setDebugMode); - router.post('/api/v1/apps/:id/configure/mailbox', appsManageScope, routes.apps.setMailbox); - router.post('/api/v1/apps/:id/configure/env', appsManageScope, routes.apps.setEnvironment); - router.post('/api/v1/apps/:id/configure/data_dir', appsManageScope, routes.apps.setDataDir); - router.post('/api/v1/apps/:id/configure/location', appsManageScope, routes.apps.setLocation); + router.post('/api/v1/apps/:id/configure/access_restriction', token, authorizeAdmin, routes.apps.setAccessRestriction); + router.post('/api/v1/apps/:id/configure/label', token, authorizeAdmin, routes.apps.setLabel); + router.post('/api/v1/apps/:id/configure/tags', token, authorizeAdmin, routes.apps.setTags); + router.post('/api/v1/apps/:id/configure/icon', token, authorizeAdmin, routes.apps.setIcon); + router.post('/api/v1/apps/:id/configure/memory_limit', token, authorizeAdmin, routes.apps.setMemoryLimit); + router.post('/api/v1/apps/:id/configure/cpu_shares', token, authorizeAdmin, routes.apps.setCpuShares); + router.post('/api/v1/apps/:id/configure/automatic_backup', token, authorizeAdmin, routes.apps.setAutomaticBackup); + router.post('/api/v1/apps/:id/configure/automatic_update', token, authorizeAdmin, routes.apps.setAutomaticUpdate); + router.post('/api/v1/apps/:id/configure/reverse_proxy', token, authorizeAdmin, routes.apps.setReverseProxyConfig); + router.post('/api/v1/apps/:id/configure/cert', token, authorizeAdmin, routes.apps.setCertificate); + router.post('/api/v1/apps/:id/configure/debug_mode', token, authorizeAdmin, routes.apps.setDebugMode); + router.post('/api/v1/apps/:id/configure/mailbox', token, authorizeAdmin, routes.apps.setMailbox); + router.post('/api/v1/apps/:id/configure/env', token, authorizeAdmin, routes.apps.setEnvironment); + router.post('/api/v1/apps/:id/configure/data_dir', token, authorizeAdmin, routes.apps.setDataDir); + router.post('/api/v1/apps/:id/configure/location', token, authorizeAdmin, routes.apps.setLocation); - router.post('/api/v1/apps/:id/repair', appsManageScope, routes.apps.repairApp); - router.post('/api/v1/apps/:id/update', appsManageScope, routes.apps.updateApp); - router.post('/api/v1/apps/:id/restore', appsManageScope, routes.apps.restoreApp); - router.post('/api/v1/apps/:id/import', appsManageScope, routes.apps.importApp); - router.post('/api/v1/apps/:id/backup', appsManageScope, routes.apps.backupApp); - router.get ('/api/v1/apps/:id/backups', appsManageScope, routes.apps.listBackups); - router.post('/api/v1/apps/:id/stop', appsManageScope, routes.apps.stopApp); - router.post('/api/v1/apps/:id/start', appsManageScope, routes.apps.startApp); - router.post('/api/v1/apps/:id/restart', appsManageScope, routes.apps.restartApp); - router.get ('/api/v1/apps/:id/logstream', appsManageScope, routes.apps.getLogStream); - router.get ('/api/v1/apps/:id/logs', appsManageScope, routes.apps.getLogs); - router.get ('/api/v1/apps/:id/exec', appsManageScope, routes.apps.exec); + router.post('/api/v1/apps/:id/repair', token, authorizeAdmin, routes.apps.repairApp); + router.post('/api/v1/apps/:id/update', token, authorizeAdmin, routes.apps.updateApp); + router.post('/api/v1/apps/:id/restore', token, authorizeAdmin, routes.apps.restoreApp); + router.post('/api/v1/apps/:id/import', token, authorizeAdmin, routes.apps.importApp); + router.post('/api/v1/apps/:id/backup', token, authorizeAdmin, routes.apps.backupApp); + router.get ('/api/v1/apps/:id/backups', token, authorizeAdmin, routes.apps.listBackups); + router.post('/api/v1/apps/:id/stop', token, authorizeAdmin, routes.apps.stopApp); + router.post('/api/v1/apps/:id/start', token, authorizeAdmin, routes.apps.startApp); + router.post('/api/v1/apps/:id/restart', token, authorizeAdmin, routes.apps.restartApp); + router.get ('/api/v1/apps/:id/logstream', token, authorizeAdmin, routes.apps.getLogStream); + router.get ('/api/v1/apps/:id/logs', token, authorizeAdmin, routes.apps.getLogs); + router.get ('/api/v1/apps/:id/exec', token, authorizeAdmin, routes.apps.exec); // websocket cannot do bearer authentication - router.get ('/api/v1/apps/:id/execws', routes.accesscontrol.websocketAuth.bind(null, [ accesscontrol.SCOPE_APPS_MANAGE ]), routes.apps.execWebSocket); - router.post('/api/v1/apps/:id/clone', appsManageScope, routes.apps.cloneApp); - router.get ('/api/v1/apps/:id/download', appsManageScope, routes.apps.downloadFile); - router.post('/api/v1/apps/:id/upload', appsManageScope, multipart, routes.apps.uploadFile); + router.get ('/api/v1/apps/:id/execws', routes.accesscontrol.websocketAuth.bind(null, ROLE_ADMIN), routes.apps.execWebSocket); + router.post('/api/v1/apps/:id/clone', token, authorizeAdmin, routes.apps.cloneApp); + router.get ('/api/v1/apps/:id/download', token, authorizeAdmin, routes.apps.downloadFile); + router.post('/api/v1/apps/:id/upload', token, authorizeAdmin, multipart, routes.apps.uploadFile); // settings routes (these are for the settings tab - avatar & name have public routes for normal users. see above) - router.get ('/api/v1/settings/:setting', settingsScope, routes.settings.get); - router.post('/api/v1/settings/:setting', settingsScope, (req, res, next) => { + router.get ('/api/v1/settings/:setting', token, authorizeAdmin, routes.settings.get); + router.post('/api/v1/settings/:setting', token, authorizeAdmin, (req, res, next) => { return req.params.setting === 'cloudron_avatar' ? multipart(req, res, next) : next(); }, routes.settings.set); // email routes - router.get ('/api/v1/mail/:domain', mailScope, routes.mail.getDomain); - router.post('/api/v1/mail', mailScope, routes.mail.addDomain); - router.get ('/api/v1/mail/:domain/stats', mailScope, routes.mail.getDomainStats); - router.del ('/api/v1/mail/:domain', mailScope, routes.mail.removeDomain); - router.get ('/api/v1/mail/:domain/status', mailScope, routes.mail.getStatus); - router.post('/api/v1/mail/:domain/mail_from_validation', mailScope, routes.mail.setMailFromValidation); - router.post('/api/v1/mail/:domain/catch_all', mailScope, routes.mail.setCatchAllAddress); - router.post('/api/v1/mail/:domain/relay', mailScope, routes.mail.setMailRelay); - router.post('/api/v1/mail/:domain/enable', mailScope, routes.mail.setMailEnabled); - router.post('/api/v1/mail/:domain/dns', mailScope, routes.mail.setDnsRecords); - router.post('/api/v1/mail/:domain/send_test_mail', mailScope, routes.mail.sendTestMail); - router.get ('/api/v1/mail/:domain/mailboxes', mailScope, routes.mail.listMailboxes); - router.get ('/api/v1/mail/:domain/mailboxes/:name', mailScope, routes.mail.getMailbox); - router.post('/api/v1/mail/:domain/mailboxes', mailScope, routes.mail.addMailbox); - router.post('/api/v1/mail/:domain/mailboxes/:name', mailScope, routes.mail.updateMailbox); - router.del ('/api/v1/mail/:domain/mailboxes/:name', mailScope, routes.mail.removeMailbox); - router.get ('/api/v1/mail/:domain/aliases', mailScope, routes.mail.listAliases); - router.get ('/api/v1/mail/:domain/aliases/:name', mailScope, routes.mail.getAliases); - router.put ('/api/v1/mail/:domain/aliases/:name', mailScope, routes.mail.setAliases); - router.get ('/api/v1/mail/:domain/lists', mailScope, routes.mail.getLists); - router.post('/api/v1/mail/:domain/lists', mailScope, routes.mail.addList); - router.get ('/api/v1/mail/:domain/lists/:name', mailScope, routes.mail.getList); - router.post('/api/v1/mail/:domain/lists/:name', mailScope, routes.mail.updateList); - router.del ('/api/v1/mail/:domain/lists/:name', mailScope, routes.mail.removeList); + router.get ('/api/v1/mail/:domain', token, authorizeAdmin, routes.mail.getDomain); + router.post('/api/v1/mail', token, authorizeAdmin, routes.mail.addDomain); + router.get ('/api/v1/mail/:domain/stats', token, authorizeAdmin, routes.mail.getDomainStats); + router.del ('/api/v1/mail/:domain', token, authorizeAdmin, routes.mail.removeDomain); + router.get ('/api/v1/mail/:domain/status', token, authorizeAdmin, routes.mail.getStatus); + router.post('/api/v1/mail/:domain/mail_from_validation', token, authorizeAdmin, routes.mail.setMailFromValidation); + router.post('/api/v1/mail/:domain/catch_all', token, authorizeAdmin, routes.mail.setCatchAllAddress); + router.post('/api/v1/mail/:domain/relay', token, authorizeAdmin, routes.mail.setMailRelay); + router.post('/api/v1/mail/:domain/enable', token, authorizeAdmin, routes.mail.setMailEnabled); + router.post('/api/v1/mail/:domain/dns', token, authorizeAdmin, routes.mail.setDnsRecords); + router.post('/api/v1/mail/:domain/send_test_mail', token, authorizeAdmin, routes.mail.sendTestMail); + router.get ('/api/v1/mail/:domain/mailboxes', token, authorizeAdmin, routes.mail.listMailboxes); + router.get ('/api/v1/mail/:domain/mailboxes/:name', token, authorizeAdmin, routes.mail.getMailbox); + router.post('/api/v1/mail/:domain/mailboxes', token, authorizeAdmin, routes.mail.addMailbox); + router.post('/api/v1/mail/:domain/mailboxes/:name', token, authorizeAdmin, routes.mail.updateMailbox); + router.del ('/api/v1/mail/:domain/mailboxes/:name', token, authorizeAdmin, routes.mail.removeMailbox); + router.get ('/api/v1/mail/:domain/aliases', token, authorizeAdmin, routes.mail.listAliases); + router.get ('/api/v1/mail/:domain/aliases/:name', token, authorizeAdmin, routes.mail.getAliases); + router.put ('/api/v1/mail/:domain/aliases/:name', token, authorizeAdmin, routes.mail.setAliases); + router.get ('/api/v1/mail/:domain/lists', token, authorizeAdmin, routes.mail.getLists); + router.post('/api/v1/mail/:domain/lists', token, authorizeAdmin, routes.mail.addList); + router.get ('/api/v1/mail/:domain/lists/:name', token, authorizeAdmin, routes.mail.getList); + router.post('/api/v1/mail/:domain/lists/:name', token, authorizeAdmin, routes.mail.updateList); + router.del ('/api/v1/mail/:domain/lists/:name', token, authorizeAdmin, routes.mail.removeList); // support - router.post('/api/v1/support/ticket', cloudronScope, routes.support.canCreateTicket, routes.support.createTicket); - router.get ('/api/v1/support/remote_support', cloudronScope, routes.support.getRemoteSupport); - router.post('/api/v1/support/remote_support', cloudronScope, routes.support.canEnableRemoteSupport, routes.support.enableRemoteSupport); + router.post('/api/v1/support/ticket', token, authorizeAdmin, routes.support.canCreateTicket, routes.support.createTicket); + router.get ('/api/v1/support/remote_support', token, authorizeAdmin, routes.support.getRemoteSupport); + router.post('/api/v1/support/remote_support', token, authorizeAdmin, routes.support.canEnableRemoteSupport, routes.support.enableRemoteSupport); // domain routes - router.post('/api/v1/domains', domainsManageScope, routes.domains.add); - router.get ('/api/v1/domains', domainsReadScope, routes.domains.getAll); - router.get ('/api/v1/domains/:domain', domainsManageScope, verifyDomainLock, routes.domains.get); // this is manage scope because it returns non-restricted fields - router.put ('/api/v1/domains/:domain', domainsManageScope, verifyDomainLock, routes.domains.update); - router.del ('/api/v1/domains/:domain', domainsManageScope, verifyDomainLock, routes.domains.del); - router.get ('/api/v1/domains/:domain/dns_check', domainsManageScope, routes.domains.checkDnsRecords); + router.post('/api/v1/domains', token, authorizeAdmin, routes.domains.add); + router.get ('/api/v1/domains', token, authorizeUser, routes.domains.getAll); + router.get ('/api/v1/domains/:domain', token, authorizeAdmin, verifyDomainLock, routes.domains.get); // this is manage scope because it returns non-restricted fields + router.put ('/api/v1/domains/:domain', token, authorizeAdmin, verifyDomainLock, routes.domains.update); + router.del ('/api/v1/domains/:domain', token, authorizeAdmin, verifyDomainLock, routes.domains.del); + router.get ('/api/v1/domains/:domain/dns_check', token, authorizeAdmin, routes.domains.checkDnsRecords); // addon routes - router.get ('/api/v1/services', cloudronScope, routes.services.getAll); - router.get ('/api/v1/services/:service', cloudronScope, routes.services.get); - router.post('/api/v1/services/:service', cloudronScope, routes.services.configure); - router.get ('/api/v1/services/:service/logs', cloudronScope, routes.services.getLogs); - router.get ('/api/v1/services/:service/logstream', cloudronScope, routes.services.getLogStream); - router.post('/api/v1/services/:service/restart', cloudronScope, routes.services.restart); + router.get ('/api/v1/services', token, authorizeAdmin, routes.services.getAll); + router.get ('/api/v1/services/:service', token, authorizeAdmin, routes.services.get); + router.post('/api/v1/services/:service', token, authorizeAdmin, routes.services.configure); + router.get ('/api/v1/services/:service/logs', token, authorizeAdmin, routes.services.getLogs); + router.get ('/api/v1/services/:service/logstream', token, authorizeAdmin, routes.services.getLogStream); + router.post('/api/v1/services/:service/restart', token, authorizeAdmin, routes.services.restart); // disable server socket "idle" timeout. we use the timeout middleware to handle timeouts on a route level // we rely on nginx for timeouts on the TCP level (see client_header_timeout)