From 1954c449931344baca04b126c86b00f95677a570 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Wed, 6 Apr 2016 08:19:56 -0400 Subject: Adding LDAP user filtering capability --- api/admin.go | 5 ++ config/config.json | 1 + einterfaces/ldap.go | 1 + i18n/en.json | 8 ++ model/config.go | 8 ++ utils/config.go | 16 ++++ webapp/components/admin_console/ldap_settings.jsx | 100 ++++++++++------------ webapp/i18n/en.json | 3 + 8 files changed, 85 insertions(+), 57 deletions(-) diff --git a/api/admin.go b/api/admin.go index 2990691a6..7b041619e 100644 --- a/api/admin.go +++ b/api/admin.go @@ -148,6 +148,11 @@ func saveConfig(c *Context, w http.ResponseWriter, r *http.Request) { return } + if err := utils.ValidateLdapFilter(cfg); err != nil { + c.Err = err + return + } + c.LogAudit("") utils.SaveConfig(utils.CfgFileName, cfg) diff --git a/config/config.json b/config/config.json index 62dcfcffc..27c697be0 100644 --- a/config/config.json +++ b/config/config.json @@ -135,6 +135,7 @@ "BaseDN": "", "BindUsername": "", "BindPassword": "", + "UserFilter": "", "FirstNameAttribute": "", "LastNameAttribute": "", "EmailAttribute": "", diff --git a/einterfaces/ldap.go b/einterfaces/ldap.go index 2977ab812..3917b42f8 100644 --- a/einterfaces/ldap.go +++ b/einterfaces/ldap.go @@ -12,6 +12,7 @@ type LdapInterface interface { GetUser(id string) (*model.User, *model.AppError) CheckPassword(id string, password string) *model.AppError SwitchToEmail(userId, ldapId, ldapPassword string) *model.AppError + ValidateFilter(filter string) *model.AppError } var theLdapInterface LdapInterface diff --git a/i18n/en.json b/i18n/en.json index a4c84a462..6cdeeaad2 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1823,6 +1823,14 @@ "id": "ent.ldap.do_login.user_not_registered.app_error", "translation": "User not registered on LDAP server" }, + { + "id": "ent.ldap.do_login.user_filtered.app_error", + "translation": "User is not permitted to use Mattermost. (LDAP user filter)" + }, + { + "id": "ent.ldap.validate_filter.app_error", + "translation": "Invalid LDAP Filter" + }, { "id": "ent.mfa.activate.authenticate.app_error", "translation": "Error attempting to authenticate MFA token" diff --git a/model/config.go b/model/config.go index e7ab07f8c..666b2770b 100644 --- a/model/config.go +++ b/model/config.go @@ -169,6 +169,9 @@ type LdapSettings struct { BindUsername *string BindPassword *string + // Filtering + UserFilter *string + // User Mapping FirstNameAttribute *string LastNameAttribute *string @@ -366,6 +369,11 @@ func (o *Config) SetDefaults() { *o.LdapSettings.Enable = false } + if o.LdapSettings.UserFilter == nil { + o.LdapSettings.UserFilter = new(string) + *o.LdapSettings.UserFilter = "" + } + if o.ServiceSettings.SessionLengthWebInDays == nil { o.ServiceSettings.SessionLengthWebInDays = new(int) *o.ServiceSettings.SessionLengthWebInDays = 30 diff --git a/utils/config.go b/utils/config.go index 93c8ffc7c..d8f52ce49 100644 --- a/utils/config.go +++ b/utils/config.go @@ -13,6 +13,7 @@ import ( l4g "github.com/alecthomas/log4go" + "github.com/mattermost/platform/einterfaces" "github.com/mattermost/platform/model" ) @@ -167,6 +168,11 @@ func LoadConfig(fileName string) { map[string]interface{}{"Filename": fileName, "Error": err.Message})) } + if err := ValidateLdapFilter(&config); err != nil { + panic(T("utils.config.load_config.validating.panic", + map[string]interface{}{"Filename": fileName, "Error": err.Message})) + } + configureLog(&config.LogSettings) TestConnection(&config) @@ -243,3 +249,13 @@ func getClientConfig(c *model.Config) map[string]string { return props } + +func ValidateLdapFilter(cfg *model.Config) *model.AppError { + ldapInterface := einterfaces.GetLdapInterface() + if *cfg.LdapSettings.Enable && ldapInterface != nil && *cfg.LdapSettings.UserFilter != "" { + if err := ldapInterface.ValidateFilter(*cfg.LdapSettings.UserFilter); err != nil { + return err + } + } + return nil +} diff --git a/webapp/components/admin_console/ldap_settings.jsx b/webapp/components/admin_console/ldap_settings.jsx index 7996a3aab..190f707b9 100644 --- a/webapp/components/admin_console/ldap_settings.jsx +++ b/webapp/components/admin_console/ldap_settings.jsx @@ -4,56 +4,14 @@ import $ from 'jquery'; import ReactDOM from 'react-dom'; import * as Client from 'utils/client.jsx'; +import * as Utils from 'utils/utils.jsx'; import * as AsyncClient from 'utils/async_client.jsx'; -import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl'; +import {FormattedMessage, FormattedHTMLMessage} from 'react-intl'; const DEFAULT_LDAP_PORT = 389; const DEFAULT_QUERY_TIMEOUT = 60; -var holders = defineMessages({ - serverEx: { - id: 'admin.ldap.serverEx', - defaultMessage: 'Ex "10.0.0.23"' - }, - portEx: { - id: 'admin.ldap.portEx', - defaultMessage: 'Ex "389"' - }, - baseEx: { - id: 'admin.ldap.baseEx', - defaultMessage: 'Ex "ou=Unit Name,dc=corp,dc=example,dc=com"' - }, - firstnameAttrEx: { - id: 'admin.ldap.firstnameAttrEx', - defaultMessage: 'Ex "givenName"' - }, - lastnameAttrEx: { - id: 'admin.ldap.lastnameAttrEx', - defaultMessage: 'Ex "sn"' - }, - emailAttrEx: { - id: 'admin.ldap.emailAttrEx', - defaultMessage: 'Ex "mail" or "userPrincipalName"' - }, - usernameAttrEx: { - id: 'admin.ldap.usernameAttrEx', - defaultMessage: 'Ex "sAMAccountName"' - }, - idAttrEx: { - id: 'admin.ldap.idAttrEx', - defaultMessage: 'Ex "sAMAccountName"' - }, - queryEx: { - id: 'admin.ldap.queryEx', - defaultMessage: 'Ex "60"' - }, - saving: { - id: 'admin.ldap.saving', - defaultMessage: 'Saving Config...' - } -}); - import React from 'react'; class LdapSettings extends React.Component { @@ -102,6 +60,7 @@ class LdapSettings extends React.Component { config.LdapSettings.EmailAttribute = this.refs.EmailAttribute.value.trim(); config.LdapSettings.UsernameAttribute = this.refs.UsernameAttribute.value.trim(); config.LdapSettings.IdAttribute = this.refs.IdAttribute.value.trim(); + config.LdapSettings.UserFilter = this.refs.UserFilter.value.trim(); let QueryTimeout = DEFAULT_QUERY_TIMEOUT; if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.QueryTimeout).value, 10))) { @@ -129,7 +88,6 @@ class LdapSettings extends React.Component { ); } render() { - const {formatMessage} = this.props.intl; let serverError = ''; if (this.state.serverError) { serverError =
; @@ -251,7 +209,7 @@ class LdapSettings extends React.Component { className='form-control' id='LdapServer' ref='LdapServer' - placeholder={formatMessage(holders.serverEx)} + placeholder={Utils.localizeMessage('admin.ldap.serverEx', 'Ex "10.0.0.23"')} defaultValue={this.props.config.LdapSettings.LdapServer} onChange={this.handleChange} disabled={!this.state.enable} @@ -280,7 +238,7 @@ class LdapSettings extends React.Component { className='form-control' id='LdapPort' ref='LdapPort' - placeholder={formatMessage(holders.portEx)} + placeholder={Utils.localizeMessage('admin.ldap.portEx', 'Ex "389"')} defaultValue={this.props.config.LdapSettings.LdapPort} onChange={this.handleChange} disabled={!this.state.enable} @@ -309,7 +267,7 @@ class LdapSettings extends React.Component { className='form-control' id='BaseDN' ref='BaseDN' - placeholder={formatMessage(holders.baseEx)} + placeholder={Utils.localizeMessage('admin.ldap.baseEx', 'Ex "ou=Unit Name,dc=corp,dc=example,dc=com"')} defaultValue={this.props.config.LdapSettings.BaseDN} onChange={this.handleChange} disabled={!this.state.enable} @@ -380,6 +338,35 @@ class LdapSettings extends React.Component {

+
+ +
+ +

+ +

+
+