diff options
-rw-r--r-- | Dockerfile | 82 | ||||
-rw-r--r-- | client/components/main/layouts.js | 48 | ||||
-rw-r--r-- | client/components/settings/connectionMethod.jade | 8 | ||||
-rw-r--r-- | client/components/settings/connectionMethod.js | 22 | ||||
-rw-r--r-- | client/components/settings/peopleBody.jade | 22 | ||||
-rw-r--r-- | client/components/settings/peopleBody.js | 31 | ||||
-rw-r--r-- | docker-compose.yml | 121 | ||||
-rw-r--r-- | i18n/en.i18n.json | 7 | ||||
-rw-r--r-- | models/settings.js | 2 | ||||
-rw-r--r-- | models/users.js | 14 | ||||
-rw-r--r-- | sandstorm-pkgdef.capnp | 4 | ||||
-rw-r--r-- | server/migrations.js | 12 | ||||
-rw-r--r-- | server/publications/people.js | 1 | ||||
-rw-r--r-- | server/publications/users.js | 7 | ||||
-rwxr-xr-x | snap-src/bin/config | 161 |
15 files changed, 493 insertions, 49 deletions
@@ -18,12 +18,52 @@ ARG MATOMO_WITH_USERNAME ARG BROWSER_POLICY_ENABLED ARG TRUSTED_URL ARG WEBHOOKS_ATTRIBUTES +ARG OAUTH2_ENABLED ARG OAUTH2_CLIENT_ID ARG OAUTH2_SECRET ARG OAUTH2_SERVER_URL ARG OAUTH2_AUTH_ENDPOINT ARG OAUTH2_USERINFO_ENDPOINT ARG OAUTH2_TOKEN_ENDPOINT +ARG LDAP_ENABLE +ARG LDAP_PORT +ARG LDAP_HOST +ARG LDAP_BASEDN +ARG LDAP_LOGIN_FALLBACK +ARG LDAP_RECONNECT +ARG LDAP_TIMEOUT +ARG LDAP_IDLE_TIMEOUT +ARG LDAP_CONNECT_TIMEOUT +ARG LDAP_AUTHENTIFICATION +ARG LDAP_AUTHENTIFICATION_USERDN +ARG LDAP_AUTHENTIFICATION_PASSWORD +ARG LDAP_LOG_ENABLED +ARG LDAP_BACKGROUND_SYNC +ARG LDAP_BACKGROUND_SYNC_INTERVAL +ARG LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED +ARG LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS +ARG LDAP_ENCRYPTION +ARG LDAP_CA_CERT +ARG LDAP_REJECT_UNAUTHORIZED +ARG LDAP_USER_SEARCH_FILTER +ARG LDAP_USER_SEARCH_SCOPE +ARG LDAP_USER_SEARCH_FIELD +ARG LDAP_SEARCH_PAGE_SIZE +ARG LDAP_SEARCH_SIZE_LIMIT +ARG LDAP_GROUP_FILTER_ENABLE +ARG LDAP_GROUP_FILTER_OBJECTCLASS +ARG LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE +ARG LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE +ARG LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT +ARG LDAP_GROUP_FILTER_GROUP_NAME +ARG LDAP_UNIQUE_IDENTIFIER_FIELD +ARG LDAP_UTF8_NAMES_SLUGIFY +ARG LDAP_USERNAME_FIELD +ARG LDAP_MERGE_EXISTING_USERS +ARG LDAP_SYNC_USER_DATA +ARG LDAP_SYNC_USER_DATA_FIELDMAP +ARG LDAP_SYNC_GROUP_ROLES +ARG LDAP_DEFAULT_DOMAIN # Set the environment variables (defaults where required) # DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303 @@ -45,12 +85,52 @@ ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 build-essential pyth BROWSER_POLICY_ENABLED=true \ TRUSTED_URL="" \ WEBHOOKS_ATTRIBUTES="" \ + OAUTH2_ENABLED=false \ OAUTH2_CLIENT_ID="" \ OAUTH2_SECRET="" \ OAUTH2_SERVER_URL="" \ OAUTH2_AUTH_ENDPOINT="" \ OAUTH2_USERINFO_ENDPOINT="" \ - OAUTH2_TOKEN_ENDPOINT="" + OAUTH2_TOKEN_ENDPOINT="" \ + LDAP_ENABLE=false \ + LDAP_PORT=389 \ + LDAP_HOST="" \ + LDAP_BASEDN="" \ + LDAP_LOGIN_FALLBACK=false \ + LDAP_RECONNECT=true \ + LDAP_TIMEOUT=10000 \ + LDAP_IDLE_TIMEOUT=10000 \ + LDAP_CONNECT_TIMEOUT=10000 \ + LDAP_AUTHENTIFICATION=false \ + LDAP_AUTHENTIFICATION_USERDN="" \ + LDAP_AUTHENTIFICATION_PASSWORD="" \ + LDAP_LOG_ENABLED=false \ + LDAP_BACKGROUND_SYNC=false \ + LDAP_BACKGROUND_SYNC_INTERVAL=100 \ + LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false \ + LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=false \ + LDAP_ENCRYPTION=false \ + LDAP_CA_CERT="" \ + LDAP_REJECT_UNAUTHORIZED=false \ + LDAP_USER_SEARCH_FILTER="" \ + LDAP_USER_SEARCH_SCOPE="" \ + LDAP_USER_SEARCH_FIELD="" \ + LDAP_SEARCH_PAGE_SIZE=0 \ + LDAP_SEARCH_SIZE_LIMIT=0 \ + LDAP_GROUP_FILTER_ENABLE=false \ + LDAP_GROUP_FILTER_OBJECTCLASS="" \ + LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="" \ + LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="" \ + LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="" \ + LDAP_GROUP_FILTER_GROUP_NAME="" \ + LDAP_UNIQUE_IDENTIFIER_FIELD="" \ + LDAP_UTF8_NAMES_SLUGIFY=true \ + LDAP_USERNAME_FIELD="" \ + LDAP_MERGE_EXISTING_USERS=false \ + LDAP_SYNC_USER_DATA=false \ + LDAP_SYNC_USER_DATA_FIELDMAP="" \ + LDAP_SYNC_GROUP_ROLES="" \ + LDAP_DEFAULT_DOMAIN="" \ # Copy the app to the image COPY ${SRC_PATH} /home/wekan/app diff --git a/client/components/main/layouts.js b/client/components/main/layouts.js index 1d3c3690..6d1f95d4 100644 --- a/client/components/main/layouts.js +++ b/client/components/main/layouts.js @@ -6,7 +6,23 @@ const i18nTagToT9n = (i18nTag) => { return i18nTag; }; +const validator = { + set: function(obj, prop, value) { + if (prop === 'state' && value !== 'signIn') { + $('.at-form-authentication').hide(); + } else if (prop === 'state' && value === 'signIn') { + $('.at-form-authentication').show(); + } + // The default behavior to store the value + obj[prop] = value; + // Indicate success + return true; + } +}; + Template.userFormsLayout.onRendered(() => { + AccountsTemplates.state.form.keys = new Proxy(AccountsTemplates.state.form.keys, validator); + const i18nTag = navigator.language; if (i18nTag) { T9n.setLanguage(i18nTagToT9n(i18nTag)); @@ -65,37 +81,37 @@ Template.userFormsLayout.events({ } }); }, - 'submit form'(event) { - const connectionMethod = $('.select-connection').val(); - + 'click #at-btn'(event) { + /* All authentication method can be managed/called here. + !! DON'T FORGET to correctly fill the fields of the user during its creation if necessary authenticationMethod : String !! + */ + const authenticationMethodSelected = $('.select-authentication').val(); // Local account - if (connectionMethod === 'default') { + if (authenticationMethodSelected === 'password') { return; } - // TODO : find a way to block "submit #at-pwd-form" of the at_pwd_form.js + // Stop submit #at-pwd-form + event.preventDefault(); + event.stopImmediatePropagation(); - const inputs = event.target.getElementsByTagName('input'); - - const email = inputs.namedItem('at-field-username_and_email').value; - const password = inputs.namedItem('at-field-password').value; + const email = $('#at-field-username_and_email').val(); + const password = $('#at-field-password').val(); // Ldap account - if (connectionMethod === 'ldap') { + if (authenticationMethodSelected === 'ldap') { // Check if the user can use the ldap connection - Meteor.subscribe('user-connection-method', email, { + Meteor.subscribe('user-authenticationMethod', email, { onReady() { - const ldap = Users.findOne(); - - if (ldap) { + const user = Users.findOne(); + if (user === undefined || user.authenticationMethod === 'ldap') { // Use the ldap connection package Meteor.loginWithLDAP(email, password, function(error) { if (!error) { // Connection return FlowRouter.go('/'); - } else { - return error; } + return error; }); } return this.stop(); diff --git a/client/components/settings/connectionMethod.jade b/client/components/settings/connectionMethod.jade index 598dd9dd..ac4c8c64 100644 --- a/client/components/settings/connectionMethod.jade +++ b/client/components/settings/connectionMethod.jade @@ -1,6 +1,6 @@ template(name='connectionMethod') - div.at-form-connection - label Authentication method - select.select-connection - each connections + div.at-form-authentication + label {{_ 'authentication-method'}} + select.select-authentication + each authentications option(value="{{value}}") {{_ value}} diff --git a/client/components/settings/connectionMethod.js b/client/components/settings/connectionMethod.js index 4983a3ef..3d5cfd76 100644 --- a/client/components/settings/connectionMethod.js +++ b/client/components/settings/connectionMethod.js @@ -1,20 +1,20 @@ Template.connectionMethod.onCreated(function() { - this.connectionMethods = new ReactiveVar([]); + this.authenticationMethods = new ReactiveVar([]); - Meteor.call('getConnectionsEnabled', (_, result) => { + Meteor.call('getAuthenticationsEnabled', (_, result) => { if (result) { // TODO : add a management of different languages // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')}) - this.connectionMethods.set([ - {value: 'default'}, - // Gets only the connection methods availables + this.authenticationMethods.set([ + {value: 'password'}, + // Gets only the authentication methods availables ...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})), ]); } // If only the default authentication available, hides the select boxe - const content = $('.at-form-connection'); - if (!(this.connectionMethods.get().length > 1)) { + const content = $('.at-form-authentication'); + if (!(this.authenticationMethods.get().length > 1)) { content.hide(); } else { content.show(); @@ -24,11 +24,11 @@ Template.connectionMethod.onCreated(function() { Template.connectionMethod.onRendered(() => { // Moves the select boxe in the first place of the at-pwd-form div - $('.at-form-connection').detach().prependTo('.at-pwd-form'); + $('.at-form-authentication').detach().prependTo('.at-pwd-form'); }); Template.connectionMethod.helpers({ - connections() { - return Template.instance().connectionMethods.get(); + authentications() { + return Template.instance().authenticationMethods.get(); }, -}); +});
\ No newline at end of file diff --git a/client/components/settings/peopleBody.jade b/client/components/settings/peopleBody.jade index a3506a24..4d06637e 100644 --- a/client/components/settings/peopleBody.jade +++ b/client/components/settings/peopleBody.jade @@ -27,6 +27,7 @@ template(name="peopleGeneral") th {{_ 'verified'}} th {{_ 'createdAt'}} th {{_ 'active'}} + th {{_ 'authentication-method'}} th each user in peopleList +peopleRow(userId=user._id) @@ -52,6 +53,7 @@ template(name="peopleRow") | {{_ 'no'}} else | {{_ 'yes'}} + td {{_ userData.authenticationMethod }} td a.edit-user | {{_ 'edit'}} @@ -66,12 +68,18 @@ template(name="editUserPopup") | {{_ 'username'}} span.error.hide.username-taken | {{_ 'error-username-taken'}} - input.js-profile-username(type="text" value=user.username) + if isLdap + input.js-profile-username(type="text" value=user.username readonly) + else + input.js-profile-username(type="text" value=user.username) label | {{_ 'email'}} span.error.hide.email-taken | {{_ 'error-email-taken'}} - input.js-profile-email(type="email" value="{{user.emails.[0].address}}") + if isLdap + input.js-profile-email(type="email" value="{{user.emails.[0].address}}" readonly) + else + input.js-profile-email(type="email" value="{{user.emails.[0].address}}") label | {{_ 'admin'}} select.select-role.js-profile-isadmin @@ -82,9 +90,17 @@ template(name="editUserPopup") select.select-active.js-profile-isactive option(value="false") {{_ 'yes'}} option(value="true" selected="{{user.loginDisabled}}") {{_ 'no'}} + label + | {{_ 'authentication-type'}} + select.select-authenticationMethod.js-authenticationMethod + each authentications + if isSelected value + option(value="{{value}}" selected) {{_ value}} + else + option(value="{{value}}") {{_ value}} hr label | {{_ 'password'}} input.js-profile-password(type="password") - input.primary.wide(type="submit" value="{{_ 'save'}}") + input.primary.wide(type="submit" value="{{_ 'save'}}")
\ No newline at end of file diff --git a/client/components/settings/peopleBody.js b/client/components/settings/peopleBody.js index 7cc992f2..acc94081 100644 --- a/client/components/settings/peopleBody.js +++ b/client/components/settings/peopleBody.js @@ -62,10 +62,39 @@ Template.peopleRow.helpers({ }, }); +Template.editUserPopup.onCreated(function() { + this.authenticationMethods = new ReactiveVar([]); + + Meteor.call('getAuthenticationsEnabled', (_, result) => { + if (result) { + // TODO : add a management of different languages + // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')}) + this.authenticationMethods.set([ + {value: 'password'}, + // Gets only the authentication methods availables + ...Object.entries(result).filter(e => e[1]).map(e => ({value: e[0]})), + ]); + } + }); +}); + Template.editUserPopup.helpers({ user() { return Users.findOne(this.userId); }, + authentications() { + return Template.instance().authenticationMethods.get(); + }, + isSelected(match) { + const userId = Template.instance().data.userId; + const selected = Users.findOne(userId).authenticationMethod; + return selected === match; + }, + isLdap() { + const userId = Template.instance().data.userId; + const selected = Users.findOne(userId).authenticationMethod; + return selected === 'ldap'; + } }); BlazeComponent.extendComponent({ @@ -91,6 +120,7 @@ Template.editUserPopup.events({ const isAdmin = tpl.find('.js-profile-isadmin').value.trim(); const isActive = tpl.find('.js-profile-isactive').value.trim(); const email = tpl.find('.js-profile-email').value.trim(); + const authentication = tpl.find('.js-authenticationMethod').value.trim(); const isChangePassword = password.length > 0; const isChangeUserName = username !== user.username; @@ -101,6 +131,7 @@ Template.editUserPopup.events({ 'profile.fullname': fullname, 'isAdmin': isAdmin === 'true', 'loginDisabled': isActive === 'true', + 'authenticationMethod': authentication }, }); diff --git a/docker-compose.yml b/docker-compose.yml index 7509bbc9..4b4cd02d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,6 +63,9 @@ 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='' + # Enable the OAuth2 connection + # example: OAUTH2_ENABLED=true + - OAUTH2_ENABLED=false # OAuth2 docs: https://github.com/wekan/wekan/wiki/OAuth2 # OAuth2 Client ID, for example from Rocket.Chat. Example: abcde12345 # example: OAUTH2_CLIENT_ID=abcde12345 @@ -82,6 +85,124 @@ services: # OAuth2 Token Endpoint. Example: /oauth/token # example: OAUTH2_TOKEN_ENDPOINT=/oauth/token - OAUTH2_TOKEN_ENDPOINT='' + # LDAP_ENABLE : Enable or not the connection by the LDAP + # example : LDAP_ENABLE=true + - LDAP_ENABLE=false + # LDAP_PORT : The port of the LDAP server + # example : LDAP_PORT=389 + - LDAP_PORT=389 + # LDAP_HOST : The host server for the LDAP server + # example : LDAP_HOST=localhost + - LDAP_HOST='' + # LDAP_BASEDN : The base DN for the LDAP Tree + # example : LDAP_BASEDN=ou=user,dc=example,dc=org + - LDAP_BASEDN='' + # LDAP_LOGIN_FALLBACK : Fallback on the default authentication method + # example : LDAP_LOGIN_FALLBACK=true + - LDAP_LOGIN_FALLBACK=false + # LDAP_RECONNECT : Reconnect to the server if the connection is lost + # example : LDAP_RECONNECT=false + - LDAP_RECONNECT=true + # LDAP_TIMEOUT : Overall timeout, in milliseconds + # example : LDAP_TIMEOUT=12345 + - LDAP_TIMEOUT=10000 + # LDAP_IDLE_TIMEOUT : Specifies the timeout for idle LDAP connections in milliseconds + # example : LDAP_IDLE_TIMEOUT=12345 + - LDAP_IDLE_TIMEOUT=10000 + # LDAP_CONNECT_TIMEOUT : Connection timeout, in milliseconds + # example : LDAP_CONNECT_TIMEOUT=12345 + - LDAP_CONNECT_TIMEOUT=10000 + # LDAP_AUTHENTIFICATION : If the LDAP needs a user account to search + # example : LDAP_AUTHENTIFICATION=true + - LDAP_AUTHENTIFICATION=false + # LDAP_AUTHENTIFICATION_USERDN : The search user DN + # example : LDAP_AUTHENTIFICATION_USERDN=cn=admin,dc=example,dc=org + - LDAP_AUTHENTIFICATION_USERDN='' + # LDAP_AUTHENTIFICATION_PASSWORD : The password for the search user + # example : AUTHENTIFICATION_PASSWORD=admin + - LDAP_AUTHENTIFICATION_PASSWORD='' + # LDAP_LOG_ENABLED : Enable logs for the module + # example : LDAP_LOG_ENABLED=true + - LDAP_LOG_ENABLED=false + # LDAP_BACKGROUND_SYNC : If the sync of the users should be done in the background + # example : LDAP_BACKGROUND_SYNC=true + - LDAP_BACKGROUND_SYNC=false + # LDAP_BACKGROUND_SYNC_INTERVAL : At which interval does the background task sync in milliseconds + # example : LDAP_BACKGROUND_SYNC_INTERVAL=12345 + - LDAP_BACKGROUND_SYNC_INTERVAL=100 + # LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED : + # example : LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=true + - LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false + # LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS : + # example : LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=true + - LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=false + # LDAP_ENCRYPTION : If using LDAPS + # example : LDAP_ENCRYPTION=true + - LDAP_ENCRYPTION=false + # LDAP_CA_CERT : The certification for the LDAPS server + # example : LDAP_CA_CERT=-----BEGIN CERTIFICATE-----MIIE+zCCA+OgAwIBAgIkAhwR/6TVLmdRY6hHxvUFWc0+Enmu/Hu6cj+G2FIdAgIC...-----END CERTIFICATE----- + - LDAP_CA_CERT='' + # LDAP_REJECT_UNAUTHORIZED : Reject Unauthorized Certificate + # example : LDAP_REJECT_UNAUTHORIZED=true + - LDAP_REJECT_UNAUTHORIZED=false + # LDAP_USER_SEARCH_FILTER : Optional extra LDAP filters. Don't forget the outmost enclosing parentheses if needed + # example : LDAP_USER_SEARCH_FILTER= + - LDAP_USER_SEARCH_FILTER='' + # LDAP_USER_SEARCH_SCOPE : Base (search only in the provided DN), one (search only in the provided DN and one level deep), or subtree (search the whole subtree) + # example : LDAP_USER_SEARCH_SCOPE=one + - LDAP_USER_SEARCH_SCOPE='' + # LDAP_USER_SEARCH_FIELD : Which field is used to find the user + # example : LDAP_USER_SEARCH_FIELD=uid + - LDAP_USER_SEARCH_FIELD='' + # LDAP_SEARCH_PAGE_SIZE : Used for pagination (0=unlimited) + # example : LDAP_SEARCH_PAGE_SIZE=12345 + - LDAP_SEARCH_PAGE_SIZE=0 + # LDAP_SEARCH_SIZE_LIMIT : The limit number of entries (0=unlimited) + # example : LDAP_SEARCH_SIZE_LIMIT=12345 + - LDAP_SEARCH_SIZE_LIMIT=0 + # LDAP_GROUP_FILTER_ENABLE : Enable group filtering + # example : LDAP_GROUP_FILTER_ENABLE=true + - LDAP_GROUP_FILTER_ENABLE=false + # LDAP_GROUP_FILTER_OBJECTCLASS : The object class for filtering + # example : LDAP_GROUP_FILTER_OBJECTCLASS=group + - LDAP_GROUP_FILTER_OBJECTCLASS='' + # LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE : + # example : + - LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE='' + # LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE : + # example : + - LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE='' + # LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT : + # example : + - LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT='' + # LDAP_GROUP_FILTER_GROUP_NAME : + # example : + - LDAP_GROUP_FILTER_GROUP_NAME='' + # LDAP_UNIQUE_IDENTIFIER_FIELD : This field is sometimes class GUID (Globally Unique Identifier) + # example : LDAP_UNIQUE_IDENTIFIER_FIELD=guid + - LDAP_UNIQUE_IDENTIFIER_FIELD='' + # LDAP_UTF8_NAMES_SLUGIFY : Convert the username to utf8 + # example : LDAP_UTF8_NAMES_SLUGIFY=false + - LDAP_UTF8_NAMES_SLUGIFY=true + # LDAP_USERNAME_FIELD : Which field contains the ldap username + # example : LDAP_USERNAME_FIELD=username + - LDAP_USERNAME_FIELD='' + # LDAP_MERGE_EXISTING_USERS : + # example : LDAP_MERGE_EXISTING_USERS=true + - LDAP_MERGE_EXISTING_USERS=false + # LDAP_SYNC_USER_DATA : + # example : LDAP_SYNC_USER_DATA=true + - LDAP_SYNC_USER_DATA=false + # LDAP_SYNC_USER_DATA_FIELDMAP : + # example : LDAP_SYNC_USER_DATA_FIELDMAP={\"cn\":\"name\", \"mail\":\"email\"} + - LDAP_SYNC_USER_DATA_FIELDMAP='' + # LDAP_SYNC_GROUP_ROLES : + # example : + - LDAP_SYNC_GROUP_ROLES='' + # LDAP_DEFAULT_DOMAIN : The default domain of the ldap it is used to create email if the field is not map correctly with the LDAP_SYNC_USER_DATA_FIELDMAP + # example : + - LDAP_DEFAULT_DOMAIN='' + depends_on: - wekandb diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 896c10a3..152f1d7c 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -607,5 +607,10 @@ "r-d-check-of-list": "of checklist", "r-d-add-checklist": "Add checklist", "r-d-remove-checklist": "Remove checklist", - "r-when-a-card-is-moved": "When a card is moved to another list" + "r-when-a-card-is-moved": "When a card is moved to another list", + "ldap": "Ldap", + "oauth2": "Oauth2", + "cas": "Cas", + "authentication-method": "Authentication method", + "authentication-type": "Authentication type" } diff --git a/models/settings.js b/models/settings.js index f7c4c85d..bb555cf9 100644 --- a/models/settings.js +++ b/models/settings.js @@ -223,7 +223,7 @@ if (Meteor.isServer) { }, // Gets all connection methods to use it in the Template - getConnectionsEnabled() { + getAuthenticationsEnabled() { return { ldap: isLdapEnabled(), oauth2: isOauth2Enabled(), diff --git a/models/users.js b/models/users.js index 27d3e9fa..9a195850 100644 --- a/models/users.js +++ b/models/users.js @@ -127,10 +127,10 @@ Users.attachSchema(new SimpleSchema({ type: Boolean, optional: true, }, - // TODO : write a migration and check if using a ldap parameter is better than a connection_type parameter - ldap: { - type: Boolean, - optional: true, + 'authenticationMethod': { + type: String, + optional: false, + defaultValue: 'password', }, })); @@ -499,6 +499,7 @@ if (Meteor.isServer) { 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 }; + user['authenticationMethod'] = 'oauth2'; // see if any existing user has this email address or username, otherwise create new const existingUser = Meteor.users.findOne({$or: [{'emails.address': email}, {'username':user.username}]}); @@ -511,6 +512,7 @@ if (Meteor.isServer) { existingUser.emails = user.emails; existingUser.username = user.username; existingUser.profile = user.profile; + existingUser['authenticationMethod'] = user['authenticationMethod']; Meteor.users.remove({_id: existingUser._id}); // remove existing record return existingUser; @@ -525,7 +527,7 @@ if (Meteor.isServer) { // If ldap, bypass the inviation code if the self registration isn't allowed. // TODO : pay attention if ldap field in the user model change to another content ex : ldap field to connection_type if (options.ldap || !disableRegistration) { - user.ldap = true; + user['authenticationMethod'] = 'ldap'; return user; } @@ -645,7 +647,7 @@ if (Meteor.isServer) { const disableRegistration = Settings.findOne().disableRegistration; // If ldap, bypass the inviation code if the self registration isn't allowed. // TODO : pay attention if ldap field in the user model change to another content ex : ldap field to connection_type - if (!doc.ldap && disableRegistration) { + if (doc['authenticationMethod'] !== 'ldap' && disableRegistration) { const invitationCode = InvitationCodes.findOne({code: doc.profile.icode, valid: true}); if (!invitationCode) { throw new Meteor.Error('error-invitation-code-not-exist'); diff --git a/sandstorm-pkgdef.capnp b/sandstorm-pkgdef.capnp index 7760ed88..206a8a35 100644 --- a/sandstorm-pkgdef.capnp +++ b/sandstorm-pkgdef.capnp @@ -245,12 +245,14 @@ const myCommand :Spk.Manifest.Command = ( (key = "BROWSER_POLICY_ENABLED", value="true"), (key = "TRUSTED_URL", value=""), (key = "WEBHOOKS_ATTRIBUTES", value=""), - (key = "OAUTH2_CLIENT_ID", value=""), + (key = "OAUTH2_ENABLED", value=""), + (key = "OAUTH2_CLIENT_ID", value="false"), (key = "OAUTH2_SECRET", value=""), (key = "OAUTH2_SERVER_URL", value=""), (key = "OAUTH2_AUTH_ENDPOINT", value=""), (key = "OAUTH2_USERINFO_ENDPOINT", value=""), (key = "OAUTH2_TOKEN_ENDPOINT", value=""), + (key = "LDAP_ENABLE", value="false"), (key = "SANDSTORM", value = "1"), (key = "METEOR_SETTINGS", value = "{\"public\": {\"sandstorm\": true}}") ] diff --git a/server/migrations.js b/server/migrations.js index 91c34be2..1d62d796 100644 --- a/server/migrations.js +++ b/server/migrations.js @@ -321,3 +321,15 @@ Migrations.add('add-subtasks-allowed', () => { }, }, noValidateMulti); }); + +Migrations.add('add-authenticationMethod', () => { + Users.update({ + 'authenticationMethod': { + $exists: false, + }, + }, { + $set: { + 'authenticationMethod': 'password', + }, + }, noValidateMulti); +}); diff --git a/server/publications/people.js b/server/publications/people.js index 7c13bdcc..022a71b5 100644 --- a/server/publications/people.js +++ b/server/publications/people.js @@ -17,6 +17,7 @@ Meteor.publish('people', function(limit) { 'emails': 1, 'createdAt': 1, 'loginDisabled': 1, + 'authenticationMethod': 1 }, }); } else { diff --git a/server/publications/users.js b/server/publications/users.js index 31c07d26..f0c94153 100644 --- a/server/publications/users.js +++ b/server/publications/users.js @@ -18,12 +18,11 @@ Meteor.publish('user-admin', function() { }); }); -Meteor.publish('user-connection-method', function(match) { +Meteor.publish('user-authenticationMethod', function(match) { check(match, String); - - return Users.find({$or: [{email: match}, {username: match}]}, { + return Users.find({$or: [{_id: match}, {email: match}, {username: match}]}, { fields: { - ldap: 1, + 'authenticationMethod': 1, }, }); }); diff --git a/snap-src/bin/config b/snap-src/bin/config index a54b13c2..076a2a57 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 OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT" +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_ENABLED OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT LDAP_ENABLE LDAP_PORT LDAP_HOST LDAP_BASEDN LDAP_LOGIN_FALLBACK LDAP_RECONNECT LDAP_TIMEOUT LDAP_IDLE_TIMEOUT LDAP_CONNECT_TIMEOUT LDAP_AUTHENTIFICATION LDAP_AUTHENTIFICATION_USERDN LDAP_AUTHENTIFICATION_PASSWORD LDAP_LOG_ENABLED LDAP_BACKGROUND_SYNC LDAP_BACKGROUND_SYNC_INTERVAL LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS LDAP_ENCRYPTION LDAP_CA_CERT LDAP_REJECT_UNAUTHORIZED LDAP_USER_SEARCH_FILTER LDAP_USER_SEARCH_SCOPE LDAP_USER_SEARCH_FIELD LDAP_SEARCH_PAGE_SIZE LDAP_SEARCH_SIZE_LIMIT LDAP_GROUP_FILTER_ENABLE LDAP_GROUP_FILTER_OBJECTCLASS LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT LDAP_GROUP_FILTER_GROUP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD LDAP_UTF8_NAMES_SLUGIFY LDAP_USERNAME_FIELD LDAP_MERGE_EXISTING_USERS LDAP_SYNC_USER_DATA LDAP_SYNC_USER_DATA_FIELDMAP LDAP_SYNC_GROUP_ROLES LDAP_DEFAULT_DOMAIN" # default values DESCRIPTION_MONGODB_BIND_UNIX_SOCKET="mongodb binding unix socket:\n"\ @@ -82,6 +82,10 @@ DESCRIPTION_WEBHOOKS_ATTRIBUTES="What to send to Outgoing Webhook, or leave out. DEFAULT_WEBHOOKS_ATTRIBUTES="" KEY_WEBHOOKS_ATTRIBUTES="webhooks-attributes" +DESCRIPTION_OAUTH2_ENABLED="Enable the OAuth2 connection" +DEFAULT_OAUTH2_ENABLED="false" +KEY_OAUTH2_ENABLED="oauth2-enabled" + 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" @@ -106,3 +110,158 @@ DESCRIPTION_OAUTH2_TOKEN_ENDPOINT="OAuth2 token endpoint. Example: /oauth/token" DEFAULT_OAUTH2_TOKEN_ENDPOINT="" KEY_OAUTH2_TOKEN_ENDPOINT="oauth2-token-endpoint" +DESCRIPTION_LDAP_ENABLE="Enable or not the connection by the LDAP" +DEFAULT_LDAP_ENABLE="false" +KEY_LDAP_ENABLE="ldap-enable" + +DESCRIPTION_LDAP_PORT="The port of the LDAP server" +DEFAULT_LDAP_PORT="389" +KEY_LDAP_PORT="ldap-port" + +DESCRIPTION_LDAP_HOST="The host server for the LDAP server" +DEFAULT_LDAP_HOST="" +KEY_LDAP_HOST="ldap-host" + +DESCRIPTION_LDAP_BASEDN="The base DN for the LDAP Tree" +DEFAULT_LDAP_BASEDN="" +KEY_LDAP_BASEDN="ldap-basedn" + +DESCRIPTION_LDAP_LOGIN_FALLBACK="Fallback on the default authentication method" +DEFAULT_LDAP_LOGIN_FALLBACK="false" +KEY_LDAP_LOGIN_FALLBACK="ldap-login-fallback" + +DESCRIPTION_LDAP_RECONNECT="Reconnect to the server if the connection is lost" +DEFAULT_LDAP_RECONNECT="true" +KEY_LDAP_RECONNECT="ldap-reconnect" + +DESCRIPTION_LDAP_TIMEOUT="Overall timeout, in milliseconds." +DEFAULT_LDAP_TIMEOUT="10000" +KEY_LDAP_TIMEOUT="ldap-timeout" + +DESCRIPTION_LDAP_IDLE_TIMEOUT="Specifies the timeout for idle LDAP connections in milliseconds" +DEFAULT_LDAP_IDLE_TIMEOUT="10000" +KEY_LDAP_IDLE_TIMEOUT="ldap-idle-timeout" + +DESCRIPTION_LDAP_CONNECT_TIMEOUT="Connection timeout, in milliseconds." +DEFAULT_LDAP_CONNECT_TIMEOUT="10000" +KEY_LDAP_CONNECT_TIMEOUT="ldap-connect-timeout" + +DESCRIPTION_LDAP_AUTHENTIFICATION="If the LDAP needs a user account to search" +DEFAULT_LDAP_AUTHENTIFICATION="false" +KEY_LDAP_AUTHENTIFICATION="ldap-authentication" + +DESCRIPTION_LDAP_AUTHENTIFICATION_USERDN="The search user DN" +DEFAULT_LDAP_AUTHENTIFICATION_USERDN="" +KEY_LDAP_AUTHENTIFICATION_USERDN="ldap-authentication-userdn" + +DESCRIPTION_LDAP_AUTHENTIFICATION_PASSWORD="The password for the search user" +DEFAULT_LDAP_AUTHENTIFICATION_PASSWORD="" +KEY_LDAP_AUTHENTIFICATION_PASSWORD="ldap-authentication-password" + +DESCRIPTION_LDAP_LOG_ENABLED="Enable logs for the module" +DEFAULT_LDAP_LOG_ENABLED="false" +KEY_LDAP_LOG_ENABLED="ldap-log-enabled" + +DESCRIPTION_LDAP_BACKGROUND_SYNC="If the sync of the users should be done in the background" +DEFAULT_LDAP_BACKGROUND_SYNC="false" +KEY_LDAP_BACKGROUND_SYNC="ldap-background-sync" + +DESCRIPTION_LDAP_BACKGROUND_SYNC_INTERVAL="At which interval does the background task sync in milliseconds" +DEFAULT_LDAP_BACKGROUND_SYNC_INTERVAL="100" +KEY_LDAP_BACKGROUND_SYNC_INTERVAL="ldap-background-sync-interval" + +DESCRIPTION_LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED="" +DEFAULT_LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED="false" +KEY_LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED="ldap-background-sync-keep-existant-users-updated" + +DESCRIPTION_LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS="" +DEFAULT_LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS="false" +KEY_LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS="ldap-background-sync-import-new-users" + +DESCRIPTION_LDAP_ENCRYPTION="If using LDAPS" +DEFAULT_LDAP_ENCRYPTION="false" +KEY_LDAP_ENCRYPTION="ldap-encryption" + +DESCRIPTION_LDAP_CA_CERT="The certification for the LDAPS server" +DEFAULT_LDAP_CA_CERT="" +KEY_LDAP_CA_CERT="ldap-ca-cert" + +DESCRIPTION_LDAP_REJECT_UNAUTHORIZED="Reject Unauthorized Certificate" +DEFAULT_LDAP_REJECT_UNAUTHORIZED="false" +KEY_LDAP_REJECT_UNAUTHORIZED="ldap-reject-unauthorized" + +DESCRIPTION_LDAP_USER_SEARCH_FILTER="Optional extra LDAP filters. Don't forget the outmost enclosing parentheses if needed" +DEFAULT_LDAP_USER_SEARCH_FILTER="" +KEY_LDAP_USER_SEARCH_FILTER="ldap-user-search-filter" + +DESCRIPTION_LDAP_USER_SEARCH_SCOPE="Base (search only in the provided DN), one (search only in the provided DN and one level deep), or subtree (search the whole subtree)." +DEFAULT_LDAP_USER_SEARCH_SCOPE="" +KEY_LDAP_USER_SEARCH_SCOPE="ldap-user-search-scope" + +DESCRIPTION_LDAP_USER_SEARCH_FIELD="Which field is used to find the user" +DEFAULT_LDAP_USER_SEARCH_FIELD="" +KEY_LDAP_USER_SEARCH_FIELD="ldap-user-search-field" + +DESCRIPTION_LDAP_SEARCH_PAGE_SIZE="Used for pagination (0=unlimited)" +DEFAULT_LDAP_SEARCH_PAGE_SIZE="0" +KEY_LDAP_SEARCH_PAGE_SIZE="ldap-search-page-size" + +DESCRIPTION_LDAP_SEARCH_SIZE_LIMIT="The limit number of entries (0=unlimited)" +DEFAULT_LDAP_SEARCH_SIZE_LIMIT="0" +KEY_LDAP_SEARCH_SIZE_LIMIT="ldap-search-size-limit" + +DESCRIPTION_LDAP_GROUP_FILTER_ENABLE="Enable group filtering" +DEFAULT_LDAP_GROUP_FILTER_ENABLE="false" +KEY_LDAP_GROUP_FILTER_ENABLE="ldap-group-filter-enable" + +DESCRIPTION_LDAP_GROUP_FILTER_OBJECTCLASS="The object class for filtering" +DEFAULT_LDAP_GROUP_FILTER_OBJECTCLASS="" +KEY_LDAP_GROUP_FILTER_OBJECTCLASS="ldap-group-filter-objectclass" + +DESCRIPTION_LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="" +DEFAULT_LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="" +KEY_LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="ldap-group-filter-id-attribute" + +DESCRIPTION_LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="" +DEFAULT_LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="" +KEY_LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="ldap-group-filter-member-attribute" + +DESCRIPTION_LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="" +DEFAULT_LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="" +KEY_LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="ldap-group-filter-member-format" + +DESCRIPTION_LDAP_GROUP_FILTER_GROUP_NAME="" +DEFAULT_LDAP_GROUP_FILTER_GROUP_NAME="" +KEY_LDAP_GROUP_FILTER_GROUP_NAME="ldap-group-filter-member-format" + +DESCRIPTION_LDAP_UNIQUE_IDENTIFIER_FIELD="This field is sometimes class GUID (Globally Unique Identifier)" +DEFAULT_LDAP_UNIQUE_IDENTIFIER_FIELD="" +KEY_LDAP_UNIQUE_IDENTIFIER_FIELD="ldap-unique-identifier-field" + +DESCRIPTION_LDAP_UTF8_NAMES_SLUGIFY="Convert the username to utf8" +DEFAULT_LDAP_UTF8_NAMES_SLUGIFY="true" +KEY_LDAP_UTF8_NAMES_SLUGIFY="ldap-utf8-names-slugify" + +DESCRIPTION_LDAP_USERNAME_FIELD="Which field contains the ldap username" +DEFAULT_LDAP_USERNAME_FIELD="" +KEY_LDAP_USERNAME_FIELD="ldap-username-field" + +DESCRIPTION_LDAP_MERGE_EXISTING_USERS="" +DEFAULT_LDAP_MERGE_EXISTING_USERS="false" +KEY_LDAP_MERGE_EXISTING_USERS="ldap-merge-existing-users" + +DESCRIPTION_LDAP_SYNC_USER_DATA="" +DEFAULT_LDAP_SYNC_USER_DATA="false" +KEY_LDAP_SYNC_USER_DATA="ldap-sync-user-data" + +DESCRIPTION_LDAP_SYNC_USER_DATA_FIELDMAP="" +DEFAULT_LDAP_SYNC_USER_DATA_FIELDMAP="" +KEY_LDAP_SYNC_USER_DATA_FIELDMAP="ldap-sync-user-data-fieldmap" + +DESCRIPTION_LDAP_SYNC_GROUP_ROLES="" +DEFAULT_LDAP_SYNC_GROUP_ROLES="" +KEY_LDAP_SYNC_GROUP_ROLES="ldap-sync-group-roles" + +DESCRIPTION_LDAP_DEFAULT_DOMAIN="The default domain of the ldap it is used to create email if the field is not map correctly with the LDAP_SYNC_USER_DATA_FIELDMAP" +DEFAULT_LDAP_DEFAULT_DOMAIN="" +KEY_LDAP_DEFAULT_DOMAIN="ldap-default-domain" |