diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 59ccb824..6741e9d8 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -38,6 +38,7 @@ services: # - DEFAULT_ADMIN_NAME=Demo Demo # - DEFAULT_ADMIN_USERNAME=demo + # - INTERNAL_ACCESS_TOKEN= # - ACTIVE_USERS_LIMIT= # Set to true to show more detailed authentication error messages. diff --git a/docker-compose.yml b/docker-compose.yml index 78ce2ddb..3ab7d175 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,6 +52,7 @@ services: # - DEFAULT_ADMIN_NAME=Demo Demo # - DEFAULT_ADMIN_USERNAME=demo + # - INTERNAL_ACCESS_TOKEN= # - ACTIVE_USERS_LIMIT= # Set to true to show more detailed authentication error messages. diff --git a/server/.env.sample b/server/.env.sample index 4196895a..aa8f41c5 100644 --- a/server/.env.sample +++ b/server/.env.sample @@ -29,6 +29,7 @@ SECRET_KEY=notsecretkey # DEFAULT_ADMIN_NAME=Demo Demo # DEFAULT_ADMIN_USERNAME=demo +# INTERNAL_ACCESS_TOKEN= # ACTIVE_USERS_LIMIT= # Set to true to show more detailed authentication error messages. diff --git a/server/api/hooks/current-user/index.js b/server/api/hooks/current-user/index.js index 78f63580..0c20d3a9 100644 --- a/server/api/hooks/current-user/index.js +++ b/server/api/hooks/current-user/index.js @@ -67,25 +67,30 @@ module.exports = function defineCurrentUserHook(sails) { if (authorizationHeader && TOKEN_PATTERN.test(authorizationHeader)) { const accessToken = authorizationHeader.replace(TOKEN_PATTERN, ''); - const { httpOnlyToken } = req.cookies; + const { internalAccessToken } = sails.config.custom; - const sessionAndUser = await getSessionAndUser(accessToken, httpOnlyToken); + if (internalAccessToken && accessToken === internalAccessToken) { + req.currentUser = User.INTERNAL; + } else { + const { httpOnlyToken } = req.cookies; + const sessionAndUser = await getSessionAndUser(accessToken, httpOnlyToken); - if (sessionAndUser) { - const { session, user } = sessionAndUser; + if (sessionAndUser) { + const { session, user } = sessionAndUser; - if (user.language) { - req.setLocale(user.language); - } + if (user.language) { + req.setLocale(user.language); + } - Object.assign(req, { - currentSession: session, - currentUser: user, - }); + Object.assign(req, { + currentSession: session, + currentUser: user, + }); - if (req.isSocket) { - sails.sockets.join(req, `@accessToken:${session.accessToken}`); - sails.sockets.join(req, `@user:${user.id}`); + if (req.isSocket) { + sails.sockets.join(req, `@accessToken:${session.accessToken}`); + sails.sockets.join(req, `@user:${user.id}`); + } } } } diff --git a/server/api/models/User.js b/server/api/models/User.js index e9bcdcbb..f1089a77 100755 --- a/server/api/models/User.js +++ b/server/api/models/User.js @@ -81,8 +81,14 @@ const PERSONAL_FIELD_NAMES = [ 'defaultProjectsOrder', ]; +const INTERNAL = { + id: '_internal', + role: Roles.ADMIN, +}; + const OIDC = { id: '_oidc', + role: Roles.ADMIN, }; module.exports = { @@ -93,6 +99,7 @@ module.exports = { LANGUAGES, PRIVATE_FIELD_NAMES, PERSONAL_FIELD_NAMES, + INTERNAL, OIDC, attributes: { diff --git a/server/api/policies/is-external.js b/server/api/policies/is-external.js new file mode 100755 index 00000000..76eb4f84 --- /dev/null +++ b/server/api/policies/is-external.js @@ -0,0 +1,12 @@ +/*! + * Copyright (c) 2024 PLANKA Software GmbH + * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md + */ + +module.exports = async function isExternal(req, res, proceed) { + if (req.currentUser.id === User.INTERNAL.id) { + return res.notFound(); // Forbidden + } + + return proceed(); +}; diff --git a/server/config/custom.js b/server/config/custom.js index 5e78a403..9d5e7f4b 100644 --- a/server/config/custom.js +++ b/server/config/custom.js @@ -50,6 +50,7 @@ module.exports.custom = { defaultAdminEmail: process.env.DEFAULT_ADMIN_EMAIL && process.env.DEFAULT_ADMIN_EMAIL.toLowerCase(), + internalAccessToken: process.env.INTERNAL_ACCESS_TOKEN, activeUsersLimit: envToNumber(process.env.ACTIVE_USERS_LIMIT), showDetailedAuthErrors: process.env.SHOW_DETAILED_AUTH_ERRORS === 'true', diff --git a/server/config/policies.js b/server/config/policies.js index a54faa93..7591d036 100644 --- a/server/config/policies.js +++ b/server/config/policies.js @@ -16,17 +16,24 @@ module.exports.policies = { * */ - '*': 'is-authenticated', + '*': ['is-authenticated', 'is-external'], - 'webhooks/index': ['is-admin'], - 'webhooks/create': ['is-admin'], - 'webhooks/update': ['is-admin'], - 'webhooks/delete': ['is-admin'], + 'webhooks/index': ['is-authenticated', 'is-external', 'is-admin'], + 'webhooks/create': ['is-authenticated', 'is-external', 'is-admin'], + 'webhooks/update': ['is-authenticated', 'is-external', 'is-admin'], + 'webhooks/delete': ['is-authenticated', 'is-external', 'is-admin'], + 'users/index': 'is-authenticated', 'users/create': ['is-authenticated', 'is-admin'], + 'users/show': 'is-authenticated', + 'users/update': 'is-authenticated', + 'users/update-email': 'is-authenticated', + 'users/update-password': 'is-authenticated', + 'users/update-username': 'is-authenticated', + 'users/update-avatar': 'is-authenticated', 'users/delete': ['is-authenticated', 'is-admin'], - 'projects/create': ['is-authenticated', 'is-admin-or-project-owner'], + 'projects/create': ['is-authenticated', 'is-external', 'is-admin-or-project-owner'], 'config/show': true, 'access-tokens/create': true,