Compare commits

...
4 Commits
13 changed files with 57 additions and 13 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
# Planka # Planka
#### Elegant open source project tracking. #### Elegant open source project tracking.
![David (path)](https://img.shields.io/github/package-json/v/plankanban/planka) ![Docker Pulls](https://img.shields.io/docker/pulls/meltyshev/planka) ![GitHub](https://img.shields.io/github/license/plankanban/planka) ![David (path)](https://img.shields.io/github/package-json/v/plankanban/planka) ![Docker Pulls](https://img.shields.io/badge/docker_pulls-4M%2B-%23066da5) ![GitHub](https://img.shields.io/github/license/plankanban/planka)
![](https://raw.githubusercontent.com/plankanban/planka/master/demo.gif) ![](https://raw.githubusercontent.com/plankanban/planka/master/demo.gif)
+2 -2
View File
@@ -15,13 +15,13 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes # This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version. # to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/) # Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.2.3 version: 0.2.4
# This is the version number of the application being deployed. This version number should be # This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to # incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using. # follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes. # It is recommended to use it with quotes.
appVersion: "1.19.2" appVersion: "1.20.0"
dependencies: dependencies:
- alias: postgresql - alias: postgresql
+1 -1
View File
@@ -1 +1 @@
REACT_APP_VERSION=1.19.2 REACT_APP_VERSION=1.20.0
+4
View File
@@ -29,7 +29,11 @@ services:
# - OIDC_ISSUER= # - OIDC_ISSUER=
# - OIDC_CLIENT_ID= # - OIDC_CLIENT_ID=
# - OIDC_CLIENT_SECRET= # - OIDC_CLIENT_SECRET=
# - OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG=
# - OIDC_USERINFO_SIGNED_RESPONSE_ALG=
# - OIDC_SCOPES=openid email profile # - OIDC_SCOPES=openid email profile
# - OIDC_RESPONSE_MODE=fragment
# - OIDC_USE_DEFAULT_RESPONSE_MODE=true
# - OIDC_ADMIN_ROLES=admin # - OIDC_ADMIN_ROLES=admin
# - OIDC_EMAIL_ATTRIBUTE=email # - OIDC_EMAIL_ATTRIBUTE=email
# - OIDC_NAME_ATTRIBUTE=name # - OIDC_NAME_ATTRIBUTE=name
+4
View File
@@ -36,7 +36,11 @@ services:
# - OIDC_ISSUER= # - OIDC_ISSUER=
# - OIDC_CLIENT_ID= # - OIDC_CLIENT_ID=
# - OIDC_CLIENT_SECRET= # - OIDC_CLIENT_SECRET=
# - OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG=
# - OIDC_USERINFO_SIGNED_RESPONSE_ALG=
# - OIDC_SCOPES=openid email profile # - OIDC_SCOPES=openid email profile
# - OIDC_RESPONSE_MODE=fragment
# - OIDC_USE_DEFAULT_RESPONSE_MODE=true
# - OIDC_ADMIN_ROLES=admin # - OIDC_ADMIN_ROLES=admin
# - OIDC_EMAIL_ATTRIBUTE=email # - OIDC_EMAIL_ATTRIBUTE=email
# - OIDC_NAME_ATTRIBUTE=name # - OIDC_NAME_ATTRIBUTE=name
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "planka", "name": "planka",
"version": "1.19.2", "version": "1.20.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "planka", "name": "planka",
"version": "1.19.2", "version": "1.20.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "planka", "name": "planka",
"version": "1.19.2", "version": "1.20.0",
"private": true, "private": true,
"homepage": "https://plankanban.github.io/planka", "homepage": "https://plankanban.github.io/planka",
"repository": { "repository": {
+4
View File
@@ -27,7 +27,11 @@ SECRET_KEY=notsecretkey
# OIDC_ISSUER= # OIDC_ISSUER=
# OIDC_CLIENT_ID= # OIDC_CLIENT_ID=
# OIDC_CLIENT_SECRET= # OIDC_CLIENT_SECRET=
# OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG=
# OIDC_USERINFO_SIGNED_RESPONSE_ALG=
# OIDC_SCOPES=openid email profile # OIDC_SCOPES=openid email profile
# OIDC_RESPONSE_MODE=fragment
# OIDC_USE_DEFAULT_RESPONSE_MODE=true
# OIDC_ADMIN_ROLES=admin # OIDC_ADMIN_ROLES=admin
# OIDC_EMAIL_ATTRIBUTE=email # OIDC_EMAIL_ATTRIBUTE=email
# OIDC_NAME_ATTRIBUTE=name # OIDC_NAME_ATTRIBUTE=name
@@ -4,6 +4,9 @@ const Errors = {
INVALID_CODE_OR_NONCE: { INVALID_CODE_OR_NONCE: {
invalidCodeOrNonce: 'Invalid code or nonce', invalidCodeOrNonce: 'Invalid code or nonce',
}, },
INVALID_USERINFO_SIGNATURE: {
invalidUserinfoSignature: 'Invalid signature on userinfo due to client misconfiguration',
},
EMAIL_ALREADY_IN_USE: { EMAIL_ALREADY_IN_USE: {
emailAlreadyInUse: 'Email already in use', emailAlreadyInUse: 'Email already in use',
}, },
@@ -31,6 +34,9 @@ module.exports = {
invalidCodeOrNonce: { invalidCodeOrNonce: {
responseType: 'unauthorized', responseType: 'unauthorized',
}, },
invalidUserinfoSignature: {
responseType: 'unauthorized',
},
emailAlreadyInUse: { emailAlreadyInUse: {
responseType: 'conflict', responseType: 'conflict',
}, },
@@ -51,6 +57,7 @@ module.exports = {
sails.log.warn(`Invalid code or nonce! (IP: ${remoteAddress})`); sails.log.warn(`Invalid code or nonce! (IP: ${remoteAddress})`);
return Errors.INVALID_CODE_OR_NONCE; return Errors.INVALID_CODE_OR_NONCE;
}) })
.intercept('invalidUserinfoSignature', () => Errors.INVALID_USERINFO_SIGNATURE)
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE) .intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE)
.intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE) .intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE)
.intercept('missingValues', () => Errors.MISSING_VALUES); .intercept('missingValues', () => Errors.MISSING_VALUES);
+9 -4
View File
@@ -4,11 +4,16 @@ module.exports = {
if (sails.hooks.oidc.isActive()) { if (sails.hooks.oidc.isActive()) {
const oidcClient = sails.hooks.oidc.getClient(); const oidcClient = sails.hooks.oidc.getClient();
const authorizationUrlParams = {
scope: sails.config.custom.oidcScopes,
};
if (!sails.config.custom.oidcUseDefaultResponseMode) {
authorizationUrlParams.response_mode = sails.config.custom.oidcResponseMode;
}
oidc = { oidc = {
authorizationUrl: oidcClient.authorizationUrl({ authorizationUrl: oidcClient.authorizationUrl(authorizationUrlParams),
scope: sails.config.custom.oidcScopes,
response_mode: 'fragment',
}),
endSessionUrl: oidcClient.issuer.end_session_endpoint ? oidcClient.endSessionUrl({}) : null, endSessionUrl: oidcClient.issuer.end_session_endpoint ? oidcClient.endSessionUrl({}) : null,
isEnforced: sails.config.custom.oidcEnforced, isEnforced: sails.config.custom.oidcEnforced,
}; };
@@ -12,6 +12,7 @@ module.exports = {
exits: { exits: {
invalidCodeOrNonce: {}, invalidCodeOrNonce: {},
invalidUserinfoSignature: {},
missingValues: {}, missingValues: {},
emailAlreadyInUse: {}, emailAlreadyInUse: {},
usernameAlreadyInUse: {}, usernameAlreadyInUse: {},
@@ -34,6 +35,14 @@ module.exports = {
); );
userInfo = await client.userinfo(tokenSet); userInfo = await client.userinfo(tokenSet);
} catch (e) { } catch (e) {
if (
e instanceof SyntaxError &&
e.message.includes('Unexpected token e in JSON at position 0')
) {
sails.log.warn('Error while exchanging OIDC code: userinfo response is signed');
throw 'invalidUserinfoSignature';
}
sails.log.warn(`Error while exchanging OIDC code: ${e}`); sails.log.warn(`Error while exchanging OIDC code: ${e}`);
throw 'invalidCodeOrNonce'; throw 'invalidCodeOrNonce';
} }
+9 -2
View File
@@ -25,12 +25,19 @@ module.exports = function defineOidcHook(sails) {
const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer); const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer);
client = new issuer.Client({ const metadata = {
client_id: sails.config.custom.oidcClientId, client_id: sails.config.custom.oidcClientId,
client_secret: sails.config.custom.oidcClientSecret, client_secret: sails.config.custom.oidcClientSecret,
redirect_uris: [sails.config.custom.oidcRedirectUri], redirect_uris: [sails.config.custom.oidcRedirectUri],
response_types: ['code'], response_types: ['code'],
}); userinfo_signed_response_alg: sails.config.custom.oidcUserinfoSignedResponseAlg,
};
if (sails.config.custom.oidcIdTokenSignedResponseAlg) {
metadata.id_token_signed_response_alg = sails.config.custom.oidcIdTokenSignedResponseAlg;
}
client = new issuer.Client(metadata);
}, },
getClient() { getClient() {
+4
View File
@@ -39,7 +39,11 @@ module.exports.custom = {
oidcIssuer: process.env.OIDC_ISSUER, oidcIssuer: process.env.OIDC_ISSUER,
oidcClientId: process.env.OIDC_CLIENT_ID, oidcClientId: process.env.OIDC_CLIENT_ID,
oidcClientSecret: process.env.OIDC_CLIENT_SECRET, oidcClientSecret: process.env.OIDC_CLIENT_SECRET,
oidcIdTokenSignedResponseAlg: process.env.OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG,
oidcUserinfoSignedResponseAlg: process.env.OIDC_USERINFO_SIGNED_RESPONSE_ALG,
oidcScopes: process.env.OIDC_SCOPES || 'openid email profile', oidcScopes: process.env.OIDC_SCOPES || 'openid email profile',
oidcResponseMode: process.env.OIDC_RESPONSE_MODE || 'fragment',
oidcUseDefaultResponseMode: process.env.OIDC_USE_DEFAULT_RESPONSE_MODE === 'true',
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [], oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [],
oidcEmailAttribute: process.env.OIDC_EMAIL_ATTRIBUTE || 'email', oidcEmailAttribute: process.env.OIDC_EMAIL_ATTRIBUTE || 'email',
oidcNameAttribute: process.env.OIDC_NAME_ATTRIBUTE || 'name', oidcNameAttribute: process.env.OIDC_NAME_ATTRIBUTE || 'name',