From 1c8a00943cff236ca40b2662189102a7851d3b56 Mon Sep 17 00:00:00 2001 From: Allemand Sylvain Date: Mon, 9 Apr 2018 16:49:07 +0200 Subject: authentification oauth2 --- .meteor/packages | 1 + models/users.js | 11 +++++++++++ server/authentication.js | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/.meteor/packages b/.meteor/packages index c1b8ab88..1b64a0a8 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -31,6 +31,7 @@ kenton:accounts-sandstorm service-configuration@1.0.11 useraccounts:unstyled useraccounts:flow-routing +salleman:accounts-oidc # Utilities check@1.2.5 diff --git a/models/users.js b/models/users.js index da8ca77c..364f7fd7 100644 --- a/models/users.js +++ b/models/users.js @@ -459,6 +459,17 @@ if (Meteor.isServer) { return user; } + if (user.services.oidc) { + user.username = user.services.oidc.username; + user.emails = [{ + address: user.services.oidc.email.toLowerCase(), + verified: false, + }]; + const initials = user.services.oidc.fullname.match(/\b[a-zA-Z]/g).join('').toUpperCase(); + user.profile = { initials: initials, fullname: user.services.oidc.fullname }; + } + + if (options.from === 'admin') { user.createdThroughApi = true; return user; diff --git a/server/authentication.js b/server/authentication.js index acc101cc..03b4c464 100644 --- a/server/authentication.js +++ b/server/authentication.js @@ -54,5 +54,24 @@ Meteor.startup(() => { Authentication.checkAdminOrCondition(userId, normalAccess); }; + if (Meteor.isServer) { + ServiceConfiguration.configurations.upsert( + { service: 'oidc' }, + { + $set: { + loginStyle: 'redirect', + clientId: 'CLIENT_ID', + secret: 'SECRET', + serverUrl: 'https://my-server', + authorizationEndpoint: '/oauth/authorize', + userinfoEndpoint: '/oauth/userinfo', + tokenEndpoint: '/oauth/token', + idTokenWhitelistFields: [], + requestPermissions: ['openid'] + } + } + ); + } + }); -- cgit v1.2.3-1-g7c22 From 3e927b4e585d46d2fe66a7cd9a6de69885793302 Mon Sep 17 00:00:00 2001 From: Allemand Sylvain Date: Tue, 10 Apr 2018 11:44:54 +0200 Subject: merge oidc and local account if exists --- models/users.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/models/users.js b/models/users.js index 364f7fd7..a04021c1 100644 --- a/models/users.js +++ b/models/users.js @@ -460,15 +460,31 @@ if (Meteor.isServer) { } if (user.services.oidc) { + var email = user.services.oidc.email.toLowerCase(); + user.username = user.services.oidc.username; - user.emails = [{ - address: user.services.oidc.email.toLowerCase(), - verified: false, - }]; - const initials = user.services.oidc.fullname.match(/\b[a-zA-Z]/g).join('').toUpperCase(); + user.emails = [{ address: email, + verified: true }]; + var initials = user.services.oidc.fullname.match(/\b[a-zA-Z]/g).join('').toUpperCase(); user.profile = { initials: initials, fullname: user.services.oidc.fullname }; - } + // see if any existing user has this email address or username, otherwise create new + var existingUser = Meteor.users.findOne({$or: [{'emails.address': email}, {'username':user.username}]}); + console.log("user to create : "); + console.log(user); + if (!existingUser) + return user; + + // copy across new service info + var service = _.keys(user.services)[0]; + existingUser.services[service] = user.services[service]; + existingUser.emails = user.emails; + existingUser.username = user.username; + existingUser.profile = user.profile; + + Meteor.users.remove({_id: existingUser._id}); // remove existing record + return existingUser; + } if (options.from === 'admin') { user.createdThroughApi = true; -- cgit v1.2.3-1-g7c22 From 39312a075e5746ddeccbf3fc22df7177a86ba4d5 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sat, 25 Aug 2018 00:49:02 +0300 Subject: - [OAuth2 Login on Standalone Wekan](https://github.com/wekan/wekan/wiki/OAuth2). For example, Rocket.Chat can provide OAuth2 login to Wekan. Also, if you have Rocket.Chat using LDAP/SAML/Google/etc for logging into Rocket.Chat, then same users can login to Wekan when Rocket.Chat is providing OAuth2 login to Wekan. Thanks to salleman33 and xet7 ! Closes #234 --- .meteor/versions | 5 +++++ CHANGELOG.md | 10 ++++++++-- Dockerfile | 14 +++++++++++++- docker-compose.yml | 19 +++++++++++++++++++ models/users.js | 17 +++++++---------- server/authentication.js | 34 +++++++++++++++++++--------------- snap-src/bin/config | 27 ++++++++++++++++++++++++++- snap-src/bin/wekan-help | 42 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 139 insertions(+), 29 deletions(-) diff --git a/.meteor/versions b/.meteor/versions index f3470d97..c116172a 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -1,5 +1,6 @@ 3stack:presence@1.1.2 accounts-base@1.4.0 +accounts-oauth@1.1.15 accounts-password@1.5.0 aldeed:collection2@2.10.0 aldeed:collection2-core@1.2.0 @@ -119,6 +120,8 @@ mquandalle:mousetrap-bindglobal@0.0.1 mquandalle:perfect-scrollbar@0.6.5_2 npm-bcrypt@0.9.3 npm-mongo@2.2.33 +oauth@1.2.1 +oauth2@1.2.0 observe-sequence@1.0.16 ongoworks:speakingurl@1.1.0 ordered-dict@1.0.9 @@ -140,6 +143,8 @@ reload@1.1.11 retry@1.0.9 routepolicy@1.0.12 rzymek:fullcalendar@3.8.0 +salleman:accounts-oidc@1.0.9 +salleman:oidc@1.0.9 service-configuration@1.0.11 session@1.1.7 sha@1.0.9 diff --git a/CHANGELOG.md b/CHANGELOG.md index 696ea9a3..c94bd36f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,16 @@ # Upcoming Wekan release -This release fixes the following bugs: +This release adds the following new features: + +- [OAuth2 Login on Standalone Wekan](https://github.com/wekan/wekan/wiki/OAuth2). For example, Rocket.Chat can provide OAuth2 login to Wekan. + Also, if you have Rocket.Chat using LDAP/SAML/Google/etc for logging into Rocket.Chat, then same users can login to Wekan when + Rocket.Chat is providing OAuth2 login to Wekan. + +and fixes the following bugs: - [Move labels back to original place at minicard](https://github.com/wekan/wekan/issues/1842). -Thanks to GitHub users hever and xet7 for their contributions. +Thanks to GitHub users hever, salleman33 and xet7 for their contributions. # v1.35 2018-08-23 Wekan release diff --git a/Dockerfile b/Dockerfile index e3371d55..eae85b1e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,12 @@ ARG MATOMO_WITH_USERNAME ARG BROWSER_POLICY_ENABLED ARG TRUSTED_URL ARG WEBHOOKS_ATTRIBUTES +ARG OAUTH2_CLIENT_ID +ARG OAUTH2_SECRET +ARG OAUTH2_SERVER_URL +ARG OAUTH2_AUTH_ENDPOINT +ARG OAUTH2_USERINFO_ENDPOINT +ARG OAUTH2_TOKEN_ENDPOINT # Set the environment variables (defaults where required) # DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303 @@ -38,7 +44,13 @@ ENV BUILD_DEPS="apt-utils gnupg gosu wget curl bzip2 build-essential python git MATOMO_WITH_USERNAME=false \ BROWSER_POLICY_ENABLED=true \ TRUSTED_URL="" \ - WEBHOOKS_ATTRIBUTES="" + WEBHOOKS_ATTRIBUTES="" \ + OAUTH2_CLIENT_ID="" \ + OAUTH2_SECRET="" \ + OAUTH2_SERVER_URL="" \ + OAUTH2_AUTH_ENDPOINT="" \ + OAUTH2_USERINFO_ENDPOINT="" \ + OAUTH2_TOKEN_ENDPOINT="" # Copy the app to the image COPY ${SRC_PATH} /home/wekan/app diff --git a/docker-compose.yml b/docker-compose.yml index bf4d02cc..99633265 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,6 +63,25 @@ services: # What to send to Outgoing Webhook, or leave out. Example, that includes all that are default: cardId,listId,oldListId,boardId,comment,user,card,commentId . # example: WEBHOOKS_ATTRIBUTES=cardId,listId,oldListId,boardId,comment,user,card,commentId - WEBHOOKS_ATTRIBUTES='' + # OAuth2 docs: https://github.com/wekan/wekan/wiki/OAuth2 + # OAuth2 Client ID, for example from Rocket.Chat. Example: abcde12345 + # example: OAUTH2_CLIENT_ID=abcde12345 + - OAUTH2_CLIENT_ID='' + # OAuth2 Secret, for example from Rocket.Chat: Example: 54321abcde + # example: OAUTH2_SECRET=54321abcde + - OAUTH2_SECRET='' + # OAuth2 Server URL, for example Rocket.Chat. Example: https://chat.example.com + # example: OAUTH2_SERVER_URL=https://chat.example.com + - OAUTH2_SERVER_URL='' + # OAuth2 Authorization Endpoint. Example: /oauth/authorize + # example: OAUTH2_AUTH_ENDPOINT=/oauth/authorize + - OAUTH2_AUTH_ENDPOINT='' + # OAuth2 Userinfo Endpoint. Example: /oauth/userinfo + # example: OAUTH2_USERINFO_ENDPOINT=/oauth/userinfo + - OAUTH2_USERINFO_ENDPOINT='' + # OAuth2 Token Endpoint. Example: /oauth/token + # example: OAUTH2_TOKEN_ENDPOINT=/oauth/token + - OAUTH2_TOKEN_ENDPOINT='' depends_on: - wekandb diff --git a/models/users.js b/models/users.js index 6e83337e..1b1b79e1 100644 --- a/models/users.js +++ b/models/users.js @@ -479,23 +479,20 @@ if (Meteor.isServer) { } if (user.services.oidc) { - var email = user.services.oidc.email.toLowerCase(); - + const email = user.services.oidc.email.toLowerCase(); + user.username = user.services.oidc.username; - user.emails = [{ address: email, - verified: true }]; - var initials = user.services.oidc.fullname.match(/\b[a-zA-Z]/g).join('').toUpperCase(); - user.profile = { initials: initials, fullname: user.services.oidc.fullname }; + user.emails = [{ address: email, verified: true }]; + const initials = user.services.oidc.fullname.match(/\b[a-zA-Z]/g).join('').toUpperCase(); + user.profile = { initials, fullname: user.services.oidc.fullname }; // see if any existing user has this email address or username, otherwise create new - var existingUser = Meteor.users.findOne({$or: [{'emails.address': email}, {'username':user.username}]}); - console.log("user to create : "); - console.log(user); + const existingUser = Meteor.users.findOne({$or: [{'emails.address': email}, {'username':user.username}]}); if (!existingUser) return user; // copy across new service info - var service = _.keys(user.services)[0]; + const service = _.keys(user.services)[0]; existingUser.services[service] = user.services[service]; existingUser.emails = user.emails; existingUser.username = user.username; diff --git a/server/authentication.js b/server/authentication.js index a6872376..6310e8df 100644 --- a/server/authentication.js +++ b/server/authentication.js @@ -63,23 +63,27 @@ Meteor.startup(() => { }; if (Meteor.isServer) { - ServiceConfiguration.configurations.upsert( - { service: 'oidc' }, - { - $set: { - loginStyle: 'redirect', - clientId: 'CLIENT_ID', - secret: 'SECRET', - serverUrl: 'https://my-server', - authorizationEndpoint: '/oauth/authorize', - userinfoEndpoint: '/oauth/userinfo', - tokenEndpoint: '/oauth/token', - idTokenWhitelistFields: [], - requestPermissions: ['openid'] + + if(process.env.OAUTH2_CLIENT_ID !== '') { + + ServiceConfiguration.configurations.upsert( // eslint-disable-line no-undef + { service: 'oidc' }, + { + $set: { + loginStyle: 'redirect', + clientId: process.env.OAUTH2_CLIENT_ID, + secret: process.env.OAUTH2_SECRET, + serverUrl: process.env.OAUTH2_SERVER_URL, + authorizationEndpoint: process.env.OAUTH2_AUTH_ENDPOINT, + userinfoEndpoint: process.env.OAUTH2_USERINFO_ENDPOINT, + tokenEndpoint: process.env.OAUTH2_TOKEN_ENDPOINT, + idTokenWhitelistFields: [], + requestPermissions: ['openid'], + }, } - } - ); + ); } + } }); diff --git a/snap-src/bin/config b/snap-src/bin/config index 85b71fa7..ffc39459 100755 --- a/snap-src/bin/config +++ b/snap-src/bin/config @@ -3,7 +3,7 @@ # All supported keys are defined here together with descriptions and default values # list of supported keys -keys="MONGODB_BIND_UNIX_SOCKET MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES" +keys="MONGODB_BIND_UNIX_SOCKET MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT" # default values DESCRIPTION_MONGODB_BIND_UNIX_SOCKET="mongodb binding unix socket:\n"\ @@ -81,3 +81,28 @@ KEY_TRUSTED_URL="trusted-url" DESCRIPTION_WEBHOOKS_ATTRIBUTES="What to send to Outgoing Webhook, or leave out. Example, that includes all that are default: cardId,listId,oldListId,boardId,comment,user,card,commentId ." DEFAULT_WEBHOOKS_ATTRIBUTES="" KEY_WEBHOOKS_ATTRIBUTES="webhooks-attributes" + +DESCRIPTION_OAUTH2_CLIENT_ID="OAuth2 Client ID, for example from Rocket.Chat. Example: abcde12345" +DEFAULT_OAUTH2_CLIENT_ID="" +KEY_OAUTH2_CLIENT_ID="oauth2-client-id" + +DESCRIPTION_OAUTH2_SECRET="OAuth2 Secret, for example from Rocket.Chat: Example: 54321abcde" +DEFAULT_OAUTH2_SECRET="" +KEY_OAUTH2_SECRET="oauth2-secret" + +DESCRIPTION_OAUTH2_SERVER_URL="OAuth2 Server URL, for example Rocket.Chat. Example: https://chat.example.com" +DEFAULT_OAUTH2_SERVER_URL="" +KEY_OAUTH2_SERVER_URL="oauth2-server-url" + +DESCRIPTION_OAUTH2_AUTH_ENDPOINT="OAuth2 authorization endpoint. Example: /oauth/authorize" +DEFAULT_OAUTH2_AUTH_ENDPOINT="" +KEY_OAUTH2_AUTH_ENDPOINT="oauth2-auth-endpoint" + +DESCRIPTION_OAUTH2_USERINFO_ENDPOINT="OAuth2 userinfo endpoint. Example: /oauth/userinfo" +DEFAULT_OAUTH2_USERINFO_ENDPOINT="" +KEY_OAUTH2_USERINFO_ENDPOINT="oauth2-userinfo-endpoint" + +DESCRIPTION_OAUTH2_TOKEN_ENDPOINT="OAuth2 token endpoint. Example: /oauth/token" +DEFAULT_OAUTH2_TOKEN_ENDPOINT="" +KEY_OAUTH2_TOKEN_ENDPOINT="oauth2-token-endpoint" + diff --git a/snap-src/bin/wekan-help b/snap-src/bin/wekan-help index 5117028d..8edaf24f 100755 --- a/snap-src/bin/wekan-help +++ b/snap-src/bin/wekan-help @@ -53,6 +53,48 @@ echo -e "\t$ snap set $SNAP_NAME WEBHOOKS_ATTRIBUTES='cardId,listId,oldListId,bo echo -e "\t-Disable the Webhooks Attributes of Wekan to send all default ones:" echo -e "\t$ snap set $SNAP_NAME WEBHOOKS_ATTRIBUTES=''" echo -e "\n" +echo -e "OAuth2 Client ID, for example from Rocket.Chat. Example: abcde12345" +echo -e "To enable the OAuth2 Client ID of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_CLIENT_ID='54321abcde'" +echo -e "\t-Disable the OAuth2 Client ID of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_CLIENT_ID=''" +echo -e "\n" +echo -e "OAuth2 Secret, for example from Rocket.Chat. Example: 54321abcde" +echo -e "To enable the OAuth2 Secret of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_SECRET='54321abcde'" +echo -e "\t-Disable the OAuth2 Secret of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_SECRET=''" +echo -e "\n" +echo -e "OAuth2 Server URL, for example Rocket.Chat. Example: https://chat.example.com" +echo -e "To enable the OAuth2 Server URL of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_SERVER_URL='https://chat.example.com'" +echo -e "\t-Disable the OAuth2 Server URL of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_SERVER_URL=''" +echo -e "\n" +echo -e "OAuth2 Server URL, for example Rocket.Chat. Example: https://chat.example.com" +echo -e "To enable the OAuth2 Server URL of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_SERVER_URL='https://chat.example.com'" +echo -e "\t-Disable the OAuth2 Server URL of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_SERVER_URL=''" +echo -e "\n" +echo -e "OAuth2 Authorization Endpoint. Example: /oauth/authorize"" +echo -e "To enable the OAuth2 Authorization Endpoint of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_AUTH_ENDPOINT='/oauth/authorize'" +echo -e "\t-Disable the OAuth2 Authorization Endpoint of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_AUTH_ENDPOINT=''" +echo -e "\n" +echo -e "OAuth2 Userinfo Endpoint. Example: /oauth/userinfo"" +echo -e "To enable the OAuth2 Userinfo Endpoint of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_USERINFO_ENDPOINT='/oauth/authorize'" +echo -e "\t-Disable the OAuth2 Userinfo Endpoint of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_USERINFO_ENDPOINT=''" +echo -e "\n" +echo -e "OAuth2 Token Endpoint. Example: /oauth/token"" +echo -e "To enable the OAuth2 Token Endpoint of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_TOKEN_ENDPOINT='/oauth/token'" +echo -e "\t-Disable the OAuth2 Token Endpoint of Wekan:" +echo -e "\t$ snap set $SNAP_NAME OAUTH2_TOKEN_ENDPOINT=''" +echo -e "\n" # parse config file for supported settings keys echo -e "wekan supports settings keys" echo -e "values can be changed by calling\n$ snap set $SNAP_NAME =''" -- cgit v1.2.3-1-g7c22