From 288800eafc91d07f859c4f59588e0b646137ccb9 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Wed, 3 Oct 2018 11:50:52 +0300 Subject: [PATCH] - Add LDAP. In progress. Thanks to maximest-pierre, Akuket and xet. Related #119 --- .meteor/packages | 1 + .meteor/versions | 2 + client/components/main/layouts.jade | 1 + client/components/main/layouts.js | 41 ++++++++++++++++++- .../components/settings/connectionMethod.jade | 6 +++ .../components/settings/connectionMethod.js | 34 +++++++++++++++ models/settings.js | 33 +++++++++++++++ models/users.js | 15 +++++-- server/publications/users.js | 10 +++++ 9 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 client/components/settings/connectionMethod.jade create mode 100644 client/components/settings/connectionMethod.js diff --git a/.meteor/packages b/.meteor/packages index 55fa77273e5..3779a684beb 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -87,4 +87,5 @@ momentjs:moment@2.22.2 browser-policy-framing mquandalle:moment msavin:usercache +wekan:wekan-ldap wekan:accounts-cas diff --git a/.meteor/versions b/.meteor/versions index 5f742b3c3a1..a082fd7d450 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -179,5 +179,7 @@ useraccounts:unstyled@1.14.2 verron:autosize@3.0.8 webapp@1.4.0 webapp-hashing@1.0.9 +wekan:wekan-ldap@0.0.2 +yasaricli:slugify@0.0.7 wekan:accounts-cas@0.1.0 zimme:active-route@2.3.2 diff --git a/client/components/main/layouts.jade b/client/components/main/layouts.jade index ac7da3af70e..68876dc5ff2 100644 --- a/client/components/main/layouts.jade +++ b/client/components/main/layouts.jade @@ -18,6 +18,7 @@ template(name="userFormsLayout") img(src="{{pathFor '/wekan-logo.png'}}" alt="Wekan") section.auth-dialog +Template.dynamic(template=content) + +connectionMethod if isCas .at-form button#cas(class='at-btn submit' type='submit') {{casSignInLabel}} diff --git a/client/components/main/layouts.js b/client/components/main/layouts.js index 6d6e616db26..1d3c36901f8 100644 --- a/client/components/main/layouts.js +++ b/client/components/main/layouts.js @@ -39,7 +39,7 @@ Template.userFormsLayout.helpers({ const curLang = T9n.getLanguage() || 'en'; return t9nTag === curLang; }, - +/* isCas() { return Meteor.settings.public && Meteor.settings.public.cas && @@ -49,6 +49,7 @@ Template.userFormsLayout.helpers({ casSignInLabel() { return TAPi18n.__('casSignIn', {}, T9n.getLanguage() || 'en'); }, +*/ }); Template.userFormsLayout.events({ @@ -64,6 +65,44 @@ Template.userFormsLayout.events({ } }); }, + 'submit form'(event) { + const connectionMethod = $('.select-connection').val(); + + // Local account + if (connectionMethod === 'default') { + return; + } + + // TODO : find a way to block "submit #at-pwd-form" of the at_pwd_form.js + + const inputs = event.target.getElementsByTagName('input'); + + const email = inputs.namedItem('at-field-username_and_email').value; + const password = inputs.namedItem('at-field-password').value; + + // Ldap account + if (connectionMethod === 'ldap') { + // Check if the user can use the ldap connection + Meteor.subscribe('user-connection-method', email, { + onReady() { + const ldap = Users.findOne(); + + if (ldap) { + // Use the ldap connection package + Meteor.loginWithLDAP(email, password, function(error) { + if (!error) { + // Connection + return FlowRouter.go('/'); + } else { + return error; + } + }); + } + return this.stop(); + }, + }); + } + }, }); Template.defaultLayout.events({ diff --git a/client/components/settings/connectionMethod.jade b/client/components/settings/connectionMethod.jade new file mode 100644 index 00000000000..598dd9dd443 --- /dev/null +++ b/client/components/settings/connectionMethod.jade @@ -0,0 +1,6 @@ +template(name='connectionMethod') + div.at-form-connection + label Authentication method + select.select-connection + each connections + option(value="{{value}}") {{_ value}} diff --git a/client/components/settings/connectionMethod.js b/client/components/settings/connectionMethod.js new file mode 100644 index 00000000000..4983a3efc2d --- /dev/null +++ b/client/components/settings/connectionMethod.js @@ -0,0 +1,34 @@ +Template.connectionMethod.onCreated(function() { + this.connectionMethods = new ReactiveVar([]); + + Meteor.call('getConnectionsEnabled', (_, 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 + ...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)) { + content.hide(); + } else { + content.show(); + } + }); +}); + +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'); +}); + +Template.connectionMethod.helpers({ + connections() { + return Template.instance().connectionMethods.get(); + }, +}); diff --git a/models/settings.js b/models/settings.js index 3b9b4eaee25..f7c4c85d3be 100644 --- a/models/settings.js +++ b/models/settings.js @@ -128,6 +128,18 @@ if (Meteor.isServer) { } } + function isLdapEnabled() { + return process.env.LDAP_ENABLE === 'true'; + } + + function isOauth2Enabled() { + return process.env.OAUTH2_ENABLED === 'true'; + } + + function isCasEnabled() { + return process.env.CAS_ENABLED === 'true'; + } + Meteor.methods({ sendInvitation(emails, boards) { check(emails, [String]); @@ -197,5 +209,26 @@ if (Meteor.isServer) { withUserName: process.env.MATOMO_WITH_USERNAME || false, }; }, + + _isLdapEnabled() { + return isLdapEnabled(); + }, + + _isOauth2Enabled() { + return isOauth2Enabled(); + }, + + _isCasEnabled() { + return isCasEnabled(); + }, + + // Gets all connection methods to use it in the Template + getConnectionsEnabled() { + return { + ldap: isLdapEnabled(), + oauth2: isOauth2Enabled(), + cas: isCasEnabled(), + }; + }, }); } diff --git a/models/users.js b/models/users.js index 60e9e75927e..27d3e9fa4c8 100644 --- a/models/users.js +++ b/models/users.js @@ -127,6 +127,11 @@ 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, + }, })); Users.allow({ @@ -490,7 +495,6 @@ if (Meteor.isServer) { if (user.services.oidc) { const email = user.services.oidc.email.toLowerCase(); - user.username = user.services.oidc.username; user.emails = [{ address: email, verified: true }]; const initials = user.services.oidc.fullname.match(/\b[a-zA-Z]/g).join('').toUpperCase(); @@ -518,7 +522,10 @@ if (Meteor.isServer) { } const disableRegistration = Settings.findOne().disableRegistration; - if (!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 (options.ldap || !disableRegistration) { + user.ldap = true; return user; } @@ -636,7 +643,9 @@ if (Meteor.isServer) { //invite user to corresponding boards const disableRegistration = Settings.findOne().disableRegistration; - if (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) { const invitationCode = InvitationCodes.findOne({code: doc.profile.icode, valid: true}); if (!invitationCode) { throw new Meteor.Error('error-invitation-code-not-exist'); diff --git a/server/publications/users.js b/server/publications/users.js index 4fd98e13d01..31c07d26a71 100644 --- a/server/publications/users.js +++ b/server/publications/users.js @@ -17,3 +17,13 @@ Meteor.publish('user-admin', function() { }, }); }); + +Meteor.publish('user-connection-method', function(match) { + check(match, String); + + return Users.find({$or: [{email: match}, {username: match}]}, { + fields: { + ldap: 1, + }, + }); +});