From 21cd46df149b7ac94d99b384143f0549e512ce4a Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Tue, 8 May 2018 17:07:29 -0600 Subject: [PATCH 01/23] many improvements --- blueprints.yaml | 59 +++++++++++++++++++++++++++ login-ldap.php | 103 ++++++++++++++++++++++++++++++++++++++++-------- login-ldap.yaml | 8 +++- 3 files changed, 152 insertions(+), 18 deletions(-) diff --git a/blueprints.yaml b/blueprints.yaml index 279851b..0cbd7cc 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -29,12 +29,14 @@ form: server_section: type: section title: LDAP Server + underline: true fields: host: type: text label: Host + size: large help: Host name of the LDAP server validate: required: true @@ -43,6 +45,7 @@ form: type: number label: Port default: 389 + size: x-small help: Port to connect to host validate: required: true @@ -51,6 +54,7 @@ form: type: number label: Version default: 3 + size: x-small help: LDAP Version 3 is most popular, only change this if you know what you are doing validate: required: true @@ -93,12 +97,14 @@ form: config_section: type: section title: LDAP Configuration + underline: true fields: user_dn: type: text label: User Search DN + size: large placeholder: uid=[username],dc=company,dc=com help: String used to authenticate a user, where [username] is directly replaced by user value entered via login validate: @@ -107,12 +113,14 @@ form: data_dn: type: text label: User Data DN + size: large placeholder: dc=company,dc=com help: String used to retrieve user data. If not provided, extra LDAP user data will not be stored in Grav user account file map_username: type: text label: Username Mapping + size: large help: LDAP Attribute(s) that contains the user's username placeholder: uid validate: @@ -121,6 +129,7 @@ form: map_fullname: type: text label: User Fullname Mapping + size: large help: LDAP Attribute(s) that contains the user's full name placeholder: givenName lastName validate: @@ -129,7 +138,57 @@ form: map_email: type: text label: User Email Mapping + size: large help: LDAP Attribute that contains the user's email placeholder: mail validate: required: true + + advanced_section: + type: section + title: Advanced Configuration + underline: true + + fields: + + save_grav_user: + type: toggle + label: Save Grav user + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + + store_ldap_data: + type: toggle + label: Store LDAP data + help: If sotring a local Grav user, you can also store LDAP data to use in Grav + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + + default_access_levels.groups: + type: selectize + size: large + label: Default Groups + '@data-options': '\Grav\User\Groups::groups' + classes: fancy + validate: + type: commalist + + default_access_levels.access.site: + type: array + label: Default Site Access + multiple: false + placeholder_key: login + placeholder_value: 'true' + validate: + type: array + required: true \ No newline at end of file diff --git a/login-ldap.php b/login-ldap.php index ab13699..2eb0054 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -66,44 +66,99 @@ class LoginLDAPPlugin extends Plugin public function userLoginAuthenticate(UserLoginEvent $event) { - $username = $event->getUser(); $credentials = $event->getCredentials(); - // This gets fired for user authentication. - $username="cn=amiller,ou=users,dc=trilbymedia,dc=com"; - $credentials="gom8Jabar"; + // Get Proper username + $user_dn = $this->config->get('plugins.login-ldap.user_dn'); + $search_dn = $this->config->get('plugins.login-ldap.search_dn'); + $username = str_replace('[username]', $credentials['username'], $user_dn); + + // Get Host info + $host = $this->config->get('plugins.login-ldap.host'); + $port = $this->config->get('plugins.login-ldap.port'); + $version = $this->config->get('plugins.login-ldap.version'); + $ssl = $this->config->get('plugins.login-ldap.ssl'); + $start_tls = $this->config->get('plugins.login-ldap.start_tls'); + $opt_referrals = $this->config->get('plugins.login-ldap.opt_referrals'); + + // Set Encryption + if ((bool) $ssl) { + $encryption = 'ssl'; + } elseif ((bool) $start_tls) { + $encryption = 'tls'; + } else { + $encryption = 'none'; + } try { $ldap = Ldap::create('ext_ldap', array( - 'host' => 'ldap.trilbymedia.com', + 'host' => $host, + 'port' => $port, + 'encryption' => $encryption, + 'options' => array( + 'protocol_version' => $version, + 'referrals' => (bool) $opt_referrals, + ), )); + // Map Info + $map_username = $this->config->get('plugins.login-ldap.map_username'); + $map_fullname = $this->config->get('plugins.login-ldap.map_fullname'); + $map_email = $this->config->get('plugins.login-ldap.map_email'); - $ldap->bind($username, $credentials); + $ldap->bind($username, $credentials['password']); - - - $query = $ldap->query('dc=trilbymedia,dc=com', 'uid=amiller'); - $results = $query->execute(); - - $userdata = ['ldap' => $results[0]->getAttributes()]; - unset($userdata['ldap']['userPassword']); + $query = $ldap->query($search_dn, $map_username .'='. $credentials['username']); + $results = $query->execute()->toArray(); // Create Grav User $grav_user = User::load($username); + // Get LDAP Data + $ldap_data = array_shift($results)->getAttributes(); + $userdata = []; + + $userdata['login'] = $this->getLDAPMappedItem($map_username, $ldap_data); + $userdata['fullname'] = $this->getLDAPMappedItem($map_fullname, $ldap_data); + $userdata['email'] = $this->getLDAPMappedItem($map_email, $ldap_data); + + // Get LDAP Data if required + if ($this->config->get('plugins.login-ldap.store_ldap_data', false)) { +// $userdata['ldap'] = $ldap_data; + foreach($ldap_data as $key => $data) { + $userdata['ldap'][$key] = array_shift($data); + } + unset($userdata['ldap']['userPassword']); + } + + $grav_user->merge($userdata); + + // Set Groups + $current_groups = $grav_user->get('groups'); + if (!$current_groups) { + $groups = $this->config->get('plugins.login-ldap.default_access_levels.groups', []); + if (count($groups) > 0) { + $data['groups'] = $groups; + $grav_user->merge($data); + } + } + + // Set Access $current_access = $grav_user->get('access'); - if (!$current_access) { - $access = $this->config->get('plugins.login.user_registration.access.site', []); + $access = $this->config->get('plugins.login-ldap.default_access_levels.access.site'); + + if (!$current_access && $access) { if (count($access) > 0) { $data['access']['site'] = $access; $grav_user->merge($data); } } - $grav_user->merge($userdata); - $grav_user->save(); + // Optional save + if ($this->config->get('plugins.login-ldap.save_grav_user', false)) { + $grav_user->save(); + } $event->setUser($grav_user); @@ -115,6 +170,8 @@ class LoginLDAPPlugin extends Plugin } catch (ConnectionException $e) { print $e->getMessage(); + $this->grav['log']->error('plugin.login-ldap: ' . $username . ' - ' . $e->getMessage()); + $event->setStatus($event::AUTHENTICATION_FAILURE); $event->stopPropagation(); @@ -138,4 +195,16 @@ class LoginLDAPPlugin extends Plugin // This gets fired on user logout. } + protected function getLDAPMappedItem($map, $ldap_data) + { + $item_bits = []; + $map_bits = explode(' ', $map); + foreach($map_bits as $bit) { + if(isset($ldap_data[$bit])) { + $item_bits[] = array_shift($ldap_data[$bit]); + } + } + $item = implode(' ', $item_bits); + return $item; + } } diff --git a/login-ldap.yaml b/login-ldap.yaml index 9f7bbcf..26b6384 100644 --- a/login-ldap.yaml +++ b/login-ldap.yaml @@ -11,4 +11,10 @@ map_username: uid map_fullname: givenName lastName map_email: mail - +save_grav_user: false +store_ldap_data: false +default_access_levels: + groups: ldap_users + access: + site: + login: 'true' From f2f6676b7e388bc2e23d4aadf54b11fe6b3fbb1a Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Tue, 8 May 2018 17:39:09 -0600 Subject: [PATCH 02/23] cleanup --- login-ldap.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/login-ldap.php b/login-ldap.php index 2eb0054..5f0fd97 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -91,7 +91,7 @@ class LoginLDAPPlugin extends Plugin } try { - + /** @var Ldap $ldap */ $ldap = Ldap::create('ext_ldap', array( 'host' => $host, 'port' => $port, @@ -125,7 +125,6 @@ class LoginLDAPPlugin extends Plugin // Get LDAP Data if required if ($this->config->get('plugins.login-ldap.store_ldap_data', false)) { -// $userdata['ldap'] = $ldap_data; foreach($ldap_data as $key => $data) { $userdata['ldap'][$key] = array_shift($data); } From 17073a3f7170e4597675bf93e4df705c54858f1d Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Tue, 8 May 2018 17:48:14 -0600 Subject: [PATCH 03/23] blueprint improvements --- blueprints.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/blueprints.yaml b/blueprints.yaml index 0cbd7cc..d581d75 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -1,13 +1,13 @@ -name: Login Ldap +name: Login LDAP version: 0.1.0 -description: Allows authentication against an LDAP Server -icon: plug +description: Allows for Grav user authentication against an LDAP Server such as OpenLDAP or ActiveDirectory +icon: user-circle-o author: name: Trilby Media email: hello@trilby.media homepage: https://github.com/trilbymedia/grav-plugin-login-ldap demo: http://demo.yoursite.com -keywords: grav, plugin, etc +keywords: grav, plugin, login, ldap, active directory, authentication bugs: https://github.com/trilbymedia/grav-plugin-login-ldap/issues docs: https://github.com/trilbymedia/grav-plugin-login-ldap/blob/develop/README.md license: MIT @@ -28,7 +28,7 @@ form: server_section: type: section - title: LDAP Server + title: Server Configuration underline: true fields: @@ -110,7 +110,7 @@ form: validate: required: true - data_dn: + search_dn: type: text label: User Data DN size: large From 4c53e53d2250f0654797481ca68a132cb2403554 Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Wed, 9 May 2018 18:15:40 -0700 Subject: [PATCH 04/23] Added groups support and mapping --- login-ldap.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/login-ldap.php b/login-ldap.php index 5f0fd97..1e4ee61 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -70,6 +70,7 @@ class LoginLDAPPlugin extends Plugin // Get Proper username $user_dn = $this->config->get('plugins.login-ldap.user_dn'); + $group_dn = $this->config->get('plugins.login-ldap.group_dn'); $search_dn = $this->config->get('plugins.login-ldap.search_dn'); $username = str_replace('[username]', $credentials['username'], $user_dn); @@ -131,6 +132,20 @@ class LoginLDAPPlugin extends Plugin unset($userdata['ldap']['userPassword']); } + // Get Groups + // retrieves all extra groups for user + $query = $ldap->query($group_dn, "(&(cn=*)(memberUid=" . $credentials['username'] . "))"); + $groups = $query->execute()->toArray(); + + // retrieve current primary group for user + $query = $ldap->query($group_dn, 'gidnumber=' . $this->getLDAPMappedItem('gidNumber', $ldap_data)); + $groups = array_merge($groups, $query->execute()->toArray()); + + foreach($groups as $group) { + $attributes = $group->getAttributes(); + $userdata['ldap']['groups'][] = array_shift($attributes['cn']); + } + $grav_user->merge($userdata); // Set Groups From b1b04b727e1d409b08521682513b1e603a80753f Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Wed, 9 May 2018 19:41:40 -0600 Subject: [PATCH 05/23] updated docs and such --- README.md | 86 ++++++++++++++++++++++++++++++++++++------------- blueprints.yaml | 21 +++++++++--- login-ldap.php | 4 +++ login-ldap.yaml | 3 +- 4 files changed, 87 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 6518833..dfc3f34 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ -# Login Ldap Plugin +# Login LDAP Plugin -**This README.md file should be modified to describe the features, installation, configuration, and general usage of this plugin.** - -The **Login Ldap** Plugin is for [Grav CMS](http://github.com/getgrav/grav). Allows authentication against an LDAP Server +The **Login LDAP** Plugin for [Grav CMS](http://github.com/getgrav/grav) allows user authentication against an LDAP Server. ## Installation -Installing the Login Ldap plugin can be done in one of two ways. The GPM (Grav Package Manager) installation method enables you to quickly and easily install the plugin with a simple terminal command, while the manual method enables you to do so via a zip file. +Installing the Login LDAP plugin can be done in one of two ways. The GPM (Grav Package Manager) installation method enables you to quickly and easily install the plugin with a simple terminal command, while the manual method enables you to do so via a zip file. ### GPM Installation (Preferred) @@ -14,7 +12,7 @@ The simplest way to install this plugin is via the [Grav Package Manager (GPM)]( bin/gpm install login-ldap -This will install the Login Ldap plugin into your `/user/plugins` directory within Grav. Its files can be found under `/your/site/grav/user/plugins/login-ldap`. +This will install the Login LDAP plugin into your `/user/plugins` directory within Grav. Its files can be found under `/your/site/grav/user/plugins/login-ldap`. ### Manual Installation @@ -23,34 +21,78 @@ To install this plugin, just download the zip version of this repository and unz You should now have all the plugin files under /your/site/grav/user/plugins/login-ldap - -> NOTE: This plugin is a modular component for Grav which requires [Grav](http://github.com/getgrav/grav) and the [Error](https://github.com/getgrav/grav-plugin-error) and [Problems](https://github.com/getgrav/grav-plugin-problems) to operate. + +Before configuring this plugin, you should copy the `user/plugins/login-ldap/login-ldap.yaml` to `user/config/plugins/login-ldap.yaml` and only edit that copy. -### Admin Plugin +### Admin Installation -If you use the admin plugin, you can install directly through the admin plugin by browsing the `Plugins` tab and clicking on the `Add` button. +If you use the admin plugin, you can install directly through the admin plugin by browsing the to `Plugins` in the sidebar menu and clicking on the `Add` button. -## Configuration +Configuring the Login LDAP plugin is as easy as navigating to the `Plugins` manager, and editing the configuration options. -Before configuring this plugin, you should copy the `user/plugins/login-ldap/login-ldap.yaml` to `user/config/plugins/login-ldap.yaml` and only edit that copy. +## Configuration Options -Here is the default configuration and an explanation of available options: +The default configuration and an explanation of available options: ```yaml enabled: true +host: +port: 389 +version: 3 +ssl: false +start_tls: false +opt_referrals: false +user_dn: uid=[username],dc=company,dc=com +search_dn: dc=company,dc=com +map_username: uid +map_fullname: givenName lastName +map_email: mail + +save_grav_user: false +store_ldap_data: false +default_access_levels: + groups: ldap_users + access: + site: + login: 'true' ``` -Note that if you use the admin plugin, a file with your configuration, and named login-ldap.yaml will be saved in the `user/config/plugins/` folder once the configuration is saved in the admin. +### Server Settings + +|Key |Description | Values | +|:---------------------|:---------------------------|:-------| +|enabled|Enables the plugin | [default: **true**] \| false| +|host|The DNS name or IP address of your LDAP server | e.g. ldap.yourcompany.com | +|port|The TCP port of the host that the LDAP server runs under | [default: **389**]| +|version|LDAP Version 3 is most popular (only change this if you know what you are doing) | [default: **3**] | +|ssl|Enable SSL for the connection (typically for port 636or 3269) | true \| [default: **false**] | +|start_tls|Negotiate TLS encryption with the LDAP server (requires all traffic to be encrypted) | true \| [default: **false**] | +|opt_referrals|Sets the value of LDAP_OPT_REFERRALS (Set to "off" for Windows 2003 servers) | true \| [default: **false**] | + +### LDAP Configuration + +|Key |Description | Values | +|:---------------------|:---------------------------|:-------| +|user_dn|DN String used to authenticate a user, where `[username]` is replaced by username value entered via login | e.g. `uid=[username],dc=company,dc=com` | +|search_dn|DN String used to retrieve user data | e.g. `ou=users,dc=company,dc=com` | +|group_dn|DN String used to retrieve user group data [OPTIONAL] | e.g. `ou=groups,dc=company,dc=com` | +|map_username|LDAP Attribute(s) that contains the user's username | [default: **uid**] | +|map_fullname|LDAP Attribute(s) that contains the user's full name | [default: **givenName lastName**] | +|map_email|LDAP Attribute(s) that contains the user's email address | [default: **mail**] | + +### Advanced Configuration + +|Key |Description | Values | +|:---------------------|:---------------------------|:-------| +|save_grav_user|Store the grav user account as a local YAML account | true \| [default: **false**] | +|store_ldap_data|If storing a local Grav user, you can also store LDAP data so its available in Grav| true \| [default: **false**] | +|default_access_levels.groups|Set a default group for all users logging in via LDAP [OPTIONAL] | e.g. `ldap_users` | +|default_access_levels.access.site|The default access to assign to users logging in via LDAP | e.g. `site: [login: 'true']` | + +> Note that if you use the admin plugin, a file with your configuration will be saved in the `user/config/plugins/login-ldap.yaml`. ## Usage -**Describe how to use the plugin.** +Once properly configured, the functionality of the LDAP plugin is transparent to the user. A user will be able to login via the normal login process and have access based on their account setup. -## Credits - -**Did you incorporate third-party code? Want to thank somebody?** - -## To Do - -- [ ] Future plans, if any diff --git a/blueprints.yaml b/blueprints.yaml index d581d75..c6db021 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -12,6 +12,9 @@ bugs: https://github.com/trilbymedia/grav-plugin-login-ldap/issues docs: https://github.com/trilbymedia/grav-plugin-login-ldap/blob/develop/README.md license: MIT +dependencies: + - { name: login, version: '>=2.6.3' } + form: validation: strict fields: @@ -38,6 +41,7 @@ form: label: Host size: large help: Host name of the LDAP server + placeholder: ldap.yourcompany.com validate: required: true @@ -103,7 +107,7 @@ form: user_dn: type: text - label: User Search DN + label: User DN size: large placeholder: uid=[username],dc=company,dc=com help: String used to authenticate a user, where [username] is directly replaced by user value entered via login @@ -112,10 +116,19 @@ form: search_dn: type: text - label: User Data DN + label: User Search DN size: large - placeholder: dc=company,dc=com + placeholder: ou=users,dc=company,dc=com help: String used to retrieve user data. If not provided, extra LDAP user data will not be stored in Grav user account file + validate: + required: true + + group_dn: + type: text + label: Group Search DN + size: large + placeholder: ou=groups,dc=company,dc=com + help: String used to retrieve user group data. If not provided, extra LDAP group data will not be stored in Grav user account file map_username: type: text @@ -165,7 +178,7 @@ form: store_ldap_data: type: toggle label: Store LDAP data - help: If sotring a local Grav user, you can also store LDAP data to use in Grav + help: If storing a local Grav user, you can also store LDAP data to use in Grav highlight: 0 default: 0 options: diff --git a/login-ldap.php b/login-ldap.php index 5f0fd97..92459b3 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -81,6 +81,10 @@ class LoginLDAPPlugin extends Plugin $start_tls = $this->config->get('plugins.login-ldap.start_tls'); $opt_referrals = $this->config->get('plugins.login-ldap.opt_referrals'); + if (is_null($host)) { + throw new ConnectionException('FATAL: LDAP host entry missing in plugin configuration...'); + } + // Set Encryption if ((bool) $ssl) { $encryption = 'ssl'; diff --git a/login-ldap.yaml b/login-ldap.yaml index 26b6384..e49bd37 100644 --- a/login-ldap.yaml +++ b/login-ldap.yaml @@ -6,7 +6,8 @@ ssl: false start_tls: false opt_referrals: false user_dn: uid=[username],dc=company,dc=com -search_dn: dc=company,dc=com +search_dn: ou=users,dc=company,dc=com +group_dn: ou=groups,dc=company,dc=com map_username: uid map_fullname: givenName lastName map_email: mail From 3b66acc72a937c0f7715e7d7227b33fd5b68968b Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Wed, 9 May 2018 20:02:55 -0600 Subject: [PATCH 06/23] Updated README and render it in plugin config --- README.md | 4 +- blueprints.yaml | 330 ++++++++++++++++++++++++++---------------------- 2 files changed, 178 insertions(+), 156 deletions(-) diff --git a/README.md b/README.md index dfc3f34..c5d9112 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The **Login LDAP** Plugin for [Grav CMS](http://github.com/getgrav/grav) allows user authentication against an LDAP Server. -## Installation +### Installation Installing the Login LDAP plugin can be done in one of two ways. The GPM (Grav Package Manager) installation method enables you to quickly and easily install the plugin with a simple terminal command, while the manual method enables you to do so via a zip file. @@ -91,7 +91,7 @@ default_access_levels: > Note that if you use the admin plugin, a file with your configuration will be saved in the `user/config/plugins/login-ldap.yaml`. -## Usage +### Usage Once properly configured, the functionality of the LDAP plugin is transparent to the user. A user will be able to login via the normal login process and have access based on their account setup. diff --git a/blueprints.yaml b/blueprints.yaml index c6db021..f4954fb 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -29,179 +29,201 @@ form: validate: type: bool - server_section: - type: section - title: Server Configuration - underline: true + ldap_tabs: + type: tabs fields: - host: - type: text - label: Host - size: large - help: Host name of the LDAP server - placeholder: ldap.yourcompany.com - validate: - required: true + tab_1: + type: tab + title: Configuration Options + fields: - port: - type: number - label: Port - default: 389 - size: x-small - help: Port to connect to host - validate: - required: true + server_section: + type: section + title: Server Configuration + underline: true - version: - type: number - label: Version - default: 3 - size: x-small - help: LDAP Version 3 is most popular, only change this if you know what you are doing - validate: - required: true + fields: - ssl: - type: toggle - label: Use SSL - default: 0 - highlight: 0 - options: - 1: PLUGIN_ADMIN.YES - 0: PLUGIN_ADMIN.NO - validate: - type: bool + host: + type: text + label: Host + size: large + help: Host name of the LDAP server + placeholder: ldap.yourcompany.com + validate: + required: true - start_tls: - type: toggle - label: Negotiate TLS - help: Negotiate TLS encryption with the LDAP server (requires all traffic to be encrypted) - default: 0 - highlight: 0 - options: - 1: PLUGIN_ADMIN.YES - 0: PLUGIN_ADMIN.NO - validate: - type: bool + port: + type: number + label: Port + default: 389 + size: x-small + help: Port to connect to host + validate: + required: true - opt_referrals: - type: toggle - label: Follow Referrals - help: Sets the value of LDAP_OPT_REFERRALS (Set to "off" for Windows 2003 servers) - default: 0 - highlight: 0 - options: - 1: PLUGIN_ADMIN.YES - 0: PLUGIN_ADMIN.NO - validate: - type: bool + version: + type: number + label: Version + default: 3 + size: x-small + help: LDAP Version 3 is most popular, only change this if you know what you are doing + validate: + required: true - config_section: - type: section - title: LDAP Configuration - underline: true + ssl: + type: toggle + label: Use SSL + default: 0 + highlight: 0 + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + validate: + type: bool - fields: + start_tls: + type: toggle + label: Negotiate TLS + help: Negotiate TLS encryption with the LDAP server (requires all traffic to be encrypted) + default: 0 + highlight: 0 + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + validate: + type: bool - user_dn: - type: text - label: User DN - size: large - placeholder: uid=[username],dc=company,dc=com - help: String used to authenticate a user, where [username] is directly replaced by user value entered via login - validate: - required: true + opt_referrals: + type: toggle + label: Follow Referrals + help: Sets the value of LDAP_OPT_REFERRALS (Set to "off" for Windows 2003 servers) + default: 0 + highlight: 0 + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + validate: + type: bool - search_dn: - type: text - label: User Search DN - size: large - placeholder: ou=users,dc=company,dc=com - help: String used to retrieve user data. If not provided, extra LDAP user data will not be stored in Grav user account file - validate: - required: true + config_section: + type: section + title: LDAP Configuration + underline: true - group_dn: - type: text - label: Group Search DN - size: large - placeholder: ou=groups,dc=company,dc=com - help: String used to retrieve user group data. If not provided, extra LDAP group data will not be stored in Grav user account file + fields: - map_username: - type: text - label: Username Mapping - size: large - help: LDAP Attribute(s) that contains the user's username - placeholder: uid - validate: - required: true + user_dn: + type: text + label: User DN + size: large + placeholder: uid=[username],dc=company,dc=com + help: String used to authenticate a user, where [username] is directly replaced by user value entered via login + validate: + required: true - map_fullname: - type: text - label: User Fullname Mapping - size: large - help: LDAP Attribute(s) that contains the user's full name - placeholder: givenName lastName - validate: - required: true + search_dn: + type: text + label: User Search DN + size: large + placeholder: ou=users,dc=company,dc=com + help: String used to retrieve user data. If not provided, extra LDAP user data will not be stored in Grav user account file + validate: + required: true - map_email: - type: text - label: User Email Mapping - size: large - help: LDAP Attribute that contains the user's email - placeholder: mail - validate: - required: true + group_dn: + type: text + label: Group Search DN + size: large + placeholder: ou=groups,dc=company,dc=com + help: String used to retrieve user group data. If not provided, extra LDAP group data will not be stored in Grav user account file - advanced_section: - type: section - title: Advanced Configuration - underline: true + map_username: + type: text + label: Username Mapping + size: large + help: LDAP Attribute(s) that contains the user's username + placeholder: uid + validate: + required: true - fields: + map_fullname: + type: text + label: User Fullname Mapping + size: large + help: LDAP Attribute(s) that contains the user's full name + placeholder: givenName lastName + validate: + required: true - save_grav_user: - type: toggle - label: Save Grav user - highlight: 0 - default: 0 - options: - 1: Enabled - 0: Disabled - validate: - type: bool + map_email: + type: text + label: User Email Mapping + size: large + help: LDAP Attribute that contains the user's email + placeholder: mail + validate: + required: true - store_ldap_data: - type: toggle - label: Store LDAP data - help: If storing a local Grav user, you can also store LDAP data to use in Grav - highlight: 0 - default: 0 - options: - 1: Enabled - 0: Disabled - validate: - type: bool + advanced_section: + type: section + title: Advanced Configuration + underline: true - default_access_levels.groups: - type: selectize - size: large - label: Default Groups - '@data-options': '\Grav\User\Groups::groups' - classes: fancy - validate: - type: commalist + fields: + + save_grav_user: + type: toggle + label: Save Grav user + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + + store_ldap_data: + type: toggle + label: Store LDAP data + help: If storing a local Grav user, you can also store LDAP data to use in Grav + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + + default_access_levels.groups: + type: selectize + size: large + label: Default Groups + '@data-options': '\Grav\User\Groups::groups' + classes: fancy + validate: + type: commalist + + default_access_levels.access.site: + type: array + label: Default Site Access + multiple: false + placeholder_key: login + placeholder_value: 'true' + validate: + type: array + required: true + + tab_2: + type: tab + title: Instructions + fields: + + ldap_instructions: + type: display + markdown: true + style: vertical + file: 'plugins://login-ldap/README.md' - default_access_levels.access.site: - type: array - label: Default Site Access - multiple: false - placeholder_key: login - placeholder_value: 'true' - validate: - type: array - required: true \ No newline at end of file From d1ae2e7012899170d8f7419e61ffdf30a05a14e1 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Thu, 10 May 2018 05:12:42 -0600 Subject: [PATCH 07/23] made search_dn & group_dn optional. Added group_query --- README.md | 36 ++++++++++++++++--- blueprints.yaml | 15 ++++---- login-ldap.php | 92 ++++++++++++++++++++++++++++++------------------- login-ldap.yaml | 7 ++-- 4 files changed, 100 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index c5d9112..91b357d 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,10 @@ version: 3 ssl: false start_tls: false opt_referrals: false -user_dn: uid=[username],dc=company,dc=com -search_dn: dc=company,dc=com +user_dn: 'uid=[username],dc=company,dc=com' +search_dn: +group_dn: +group_query: '(&(cn=*)(memberUid=[username]))' map_username: uid map_fullname: givenName lastName map_email: mail @@ -74,8 +76,9 @@ default_access_levels: |Key |Description | Values | |:---------------------|:---------------------------|:-------| |user_dn|DN String used to authenticate a user, where `[username]` is replaced by username value entered via login | e.g. `uid=[username],dc=company,dc=com` | -|search_dn|DN String used to retrieve user data | e.g. `ou=users,dc=company,dc=com` | -|group_dn|DN String used to retrieve user group data [OPTIONAL] | e.g. `ou=groups,dc=company,dc=com` | +|search_dn|DN String used to retrieve user data. If not provided, extra LDAP user data will not be stored in Grav user account file [OPTIONAL]| e.g. `ou=users,dc=company,dc=com` | +|group_dn|DN String used to retrieve user group data. If not provided, extra LDAP group data will not be stored in Grav user account file [OPTIONAL] | e.g. `ou=groups,dc=company,dc=com` | +|group_query|The query used to search Groups. Only change this if you know what you are doing| e.g. `(&(cn=*)(memberUid=[username]))`| |map_username|LDAP Attribute(s) that contains the user's username | [default: **uid**] | |map_fullname|LDAP Attribute(s) that contains the user's full name | [default: **givenName lastName**] | |map_email|LDAP Attribute(s) that contains the user's email address | [default: **mail**] | @@ -95,4 +98,29 @@ default_access_levels: Once properly configured, the functionality of the LDAP plugin is transparent to the user. A user will be able to login via the normal login process and have access based on their account setup. +For the most basic of authentication, only the `user_dn` is required. This uses LDAP **bind** to simply map a full user **DN** to an entry in the LDAP directory with an associated password. If no `search_dn` is provided, once authenticated, the only information available about the user is the `username` provided during login. + +#### LDAP User Data + +In order to obtain data about the user a valid `search_dn` is required. This will search the LDAP directory at the level indicated in the DN and search for a userid with the `username` provided. the `map_username` field is used in this LDAP search query, so it's important that the `map_username` field is one that properly maps the `username` provided during login to the LDAP user entry. + +#### LDAP Group Data + +To be able to know the groups a user is associated with, a valid `group_dn` and `group_query` is required. Any invalid information will throw an exception stating that the search could not complete. + +### Storing Grav User + +By default the lDAP plugin does not store any local user information. Upon successfully authenticating against the LDAP user, a user is created and is available during the session. However, upon returning, the user must re-authenticate and the LDAP data is retrieved again. + +If you want to be able to set user data (extra fields, or specific user access) for a particular user, you can enable the `save_grav_user` option, and this will create a local Grav user in the `accounts/` folder. This is a local record of the user and attributes can be set here. + +> NOTE: Any attribute stored under the `ldap:` key in the user account file will be overwritten by the plugin during the next login. This information is always in sync with latest data in the LDAP server. The same rule goes for the **mapped** fields. So updating `email` in your LDAP directory will ensure the entry in the local Grav user is updated on next login. + +### Troubleshooting + +If a user is simply unable to authenticate against the LDAP server, an entry will be logged into the Grav log (`logs/grav.log`) file with the attempted `dn`. This can be used to ensure the `user_dn` entry is correct and can be tested against any other LDAP login system. + +If either the `user_dn`, `search_dn`, `group_dn` or `group_query` are incorrect an error will be thrown during login, and a message with the error stored in the `logs/grav.log` file. + +If you expect `fullname`, or `email` to be stored in the Grav user object, but they are not appearing, it's probably a problem with your field mappings. Double check with your LDAP administrator that these are the correct mappings. diff --git a/blueprints.yaml b/blueprints.yaml index f4954fb..bb2e930 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -130,8 +130,6 @@ form: size: large placeholder: ou=users,dc=company,dc=com help: String used to retrieve user data. If not provided, extra LDAP user data will not be stored in Grav user account file - validate: - required: true group_dn: type: text @@ -140,14 +138,19 @@ form: placeholder: ou=groups,dc=company,dc=com help: String used to retrieve user group data. If not provided, extra LDAP group data will not be stored in Grav user account file + group_query: + type: text + label: Group Query + size: large + placeholder: '(&(cn=*)(memberUid=[username]))' + help: The query used to search Groups. Only change this if you know what you are doing + map_username: type: text label: Username Mapping size: large help: LDAP Attribute(s) that contains the user's username placeholder: uid - validate: - required: true map_fullname: type: text @@ -155,8 +158,6 @@ form: size: large help: LDAP Attribute(s) that contains the user's full name placeholder: givenName lastName - validate: - required: true map_email: type: text @@ -164,8 +165,6 @@ form: size: large help: LDAP Attribute that contains the user's email placeholder: mail - validate: - required: true advanced_section: type: section diff --git a/login-ldap.php b/login-ldap.php index 0aa27fe..50ed2b7 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -69,9 +69,11 @@ class LoginLDAPPlugin extends Plugin $credentials = $event->getCredentials(); // Get Proper username - $user_dn = $this->config->get('plugins.login-ldap.user_dn'); - $group_dn = $this->config->get('plugins.login-ldap.group_dn'); - $search_dn = $this->config->get('plugins.login-ldap.search_dn'); + $user_dn = $this->config->get('plugins.login-ldap.user_dn'); + $search_dn = $this->config->get('plugins.login-ldap.search_dn'); + $group_dn = $this->config->get('plugins.login-ldap.group_dn'); + $group_query = $this->config->get('plugins.login-ldap.group_query'); + $username = str_replace('[username]', $credentials['username'], $user_dn); // Get Host info @@ -112,46 +114,61 @@ class LoginLDAPPlugin extends Plugin $map_fullname = $this->config->get('plugins.login-ldap.map_fullname'); $map_email = $this->config->get('plugins.login-ldap.map_email'); + // Try to login via LDAP $ldap->bind($username, $credentials['password']); - $query = $ldap->query($search_dn, $map_username .'='. $credentials['username']); - $results = $query->execute()->toArray(); - // Create Grav User $grav_user = User::load($username); - // Get LDAP Data - $ldap_data = array_shift($results)->getAttributes(); - $userdata = []; + // Set defaults with only thing we know... username provided + $grav_user['login'] = $credentials['username']; + $grav_user['fullname'] = $credentials['username']; - $userdata['login'] = $this->getLDAPMappedItem($map_username, $ldap_data); - $userdata['fullname'] = $this->getLDAPMappedItem($map_fullname, $ldap_data); - $userdata['email'] = $this->getLDAPMappedItem($map_email, $ldap_data); + // If search_dn is set we can try to get information from LDAP + if ($search_dn) { - // Get LDAP Data if required - if ($this->config->get('plugins.login-ldap.store_ldap_data', false)) { - foreach($ldap_data as $key => $data) { - $userdata['ldap'][$key] = array_shift($data); + $query = $ldap->query($search_dn, $map_username .'='. $credentials['username']); + $results = $query->execute()->toArray(); + + // Get LDAP Data + $ldap_data = array_shift($results)->getAttributes(); + $userdata = []; + + $userdata['login'] = $this->getLDAPMappedItem($map_username, $ldap_data); + $userdata['fullname'] = $this->getLDAPMappedItem($map_fullname, $ldap_data); + $userdata['email'] = $this->getLDAPMappedItem($map_email, $ldap_data); + + // Get LDAP Data if required + if ($this->config->get('plugins.login-ldap.store_ldap_data', false)) { + foreach($ldap_data as $key => $data) { + $userdata['ldap'][$key] = array_shift($data); + } + unset($userdata['ldap']['userPassword']); } - unset($userdata['ldap']['userPassword']); + + // Get Groups if group_dn if set + if ($group_dn) { + // retrieves all extra groups for user + $group_query = str_replace('[username]', $credentials['username'], $group_query); + $query = $ldap->query($group_dn, $group_query); + $groups = $query->execute()->toArray(); + + // retrieve current primary group for user + $query = $ldap->query($group_dn, 'gidnumber=' . $this->getLDAPMappedItem('gidNumber', $ldap_data)); + $groups = array_merge($groups, $query->execute()->toArray()); + + if ($this->config->get('plugins.login-ldap.store_ldap_data', false)) { + foreach ($groups as $group) { + $attributes = $group->getAttributes(); + $userdata['ldap']['groups'][] = array_shift($attributes['cn']); + } + } + } + + // Merge the LDAP user data with Grav user + $grav_user->merge($userdata); } - // Get Groups - // retrieves all extra groups for user - $query = $ldap->query($group_dn, "(&(cn=*)(memberUid=" . $credentials['username'] . "))"); - $groups = $query->execute()->toArray(); - - // retrieve current primary group for user - $query = $ldap->query($group_dn, 'gidnumber=' . $this->getLDAPMappedItem('gidNumber', $ldap_data)); - $groups = array_merge($groups, $query->execute()->toArray()); - - foreach($groups as $group) { - $attributes = $group->getAttributes(); - $userdata['ldap']['groups'][] = array_shift($attributes['cn']); - } - - $grav_user->merge($userdata); - // Set Groups $current_groups = $grav_user->get('groups'); if (!$current_groups) { @@ -186,9 +203,14 @@ class LoginLDAPPlugin extends Plugin return; } catch (ConnectionException $e) { - print $e->getMessage(); + $message = $e->getMessage(); - $this->grav['log']->error('plugin.login-ldap: ' . $username . ' - ' . $e->getMessage()); + $this->grav['log']->error('plugin.login-ldap: ['. $e->getCode() . '] ' . $username . ' - ' . $message); + + // Just return so other authenticators can take a shot... + if ($message = "Invalid credentials") { + return; + } $event->setStatus($event::AUTHENTICATION_FAILURE); $event->stopPropagation(); diff --git a/login-ldap.yaml b/login-ldap.yaml index e49bd37..cf720b6 100644 --- a/login-ldap.yaml +++ b/login-ldap.yaml @@ -5,9 +5,10 @@ version: 3 ssl: false start_tls: false opt_referrals: false -user_dn: uid=[username],dc=company,dc=com -search_dn: ou=users,dc=company,dc=com -group_dn: ou=groups,dc=company,dc=com +user_dn: 'uid=[username],dc=company,dc=com' +search_dn: +group_dn: +group_query: '(&(cn=*)(memberUid=[username]))' map_username: uid map_fullname: givenName lastName map_email: mail From 07898de0524e8d796164f588802c11770e542c42 Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Thu, 10 May 2018 19:14:40 -0700 Subject: [PATCH 08/23] Added support for groups and map to grav access level --- blueprints.yaml | 16 ++++++++++++++++ login-ldap.php | 24 ++++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/blueprints.yaml b/blueprints.yaml index bb2e930..25625f4 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -215,6 +215,22 @@ form: type: array required: true + default_access_levels.access.groups: + classes: frontmatter + type: editor + label: Groups Access Level + autofocus: true + default: "admin:\r\n admin:\r\n login: true\r\n super: true\r\n site:\r\n login: true\r\nuser:\r\n site:\r\n login: true" + codemirror: + mode: 'yaml' + indentUnit: 2 + autofocus: true + indentWithTabs: false + lineNumbers: true + styleActiveLine: true + gutters: ['CodeMirror-lint-markers'] + lint: true + tab_2: type: tab title: Instructions diff --git a/login-ldap.php b/login-ldap.php index 50ed2b7..b871b4b 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -7,6 +7,7 @@ use Grav\Plugin\Login\Events\UserLoginEvent; use Grav\Plugin\Login\Login; use Symfony\Component\Ldap\Ldap; use Symfony\Component\Ldap\Exception\ConnectionException; +use Symfony\Component\Yaml\Yaml; /** * Class LoginLDAPPlugin @@ -123,6 +124,7 @@ class LoginLDAPPlugin extends Plugin // Set defaults with only thing we know... username provided $grav_user['login'] = $credentials['username']; $grav_user['fullname'] = $credentials['username']; + $user_groups = []; // If search_dn is set we can try to get information from LDAP if ($search_dn) { @@ -157,10 +159,13 @@ class LoginLDAPPlugin extends Plugin $query = $ldap->query($group_dn, 'gidnumber=' . $this->getLDAPMappedItem('gidNumber', $ldap_data)); $groups = array_merge($groups, $query->execute()->toArray()); - if ($this->config->get('plugins.login-ldap.store_ldap_data', false)) { - foreach ($groups as $group) { - $attributes = $group->getAttributes(); - $userdata['ldap']['groups'][] = array_shift($attributes['cn']); + foreach ($groups as $group) { + $attributes = $group->getAttributes(); + $user_group = array_shift($attributes['cn']); + $user_groups[] = $user_group; + + if ($this->config->get('plugins.login-ldap.store_ldap_data', false)) { + $userdata['ldap']['groups'][] = $user_group; } } } @@ -190,6 +195,17 @@ class LoginLDAPPlugin extends Plugin } } + // Give Admin Access + $admin_access = $this->config->get('plugins.login-ldap.default_access_levels.access.groups'); + if ($admin_access && count($user_groups) && strlen($admin_access) > 0) { + $groups_access = Yaml::parse($admin_access); + foreach ($groups_access as $key => $group_access) { + if (in_array($key, $user_groups)) { + $grav_user->merge(['access' => $group_access]); + } + } + } + // Optional save if ($this->config->get('plugins.login-ldap.save_grav_user', false)) { $grav_user->save(); From 979da05c3d1997cc914bc3e6a6354cef91d1fa9a Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Fri, 11 May 2018 09:32:08 -0700 Subject: [PATCH 09/23] Typos --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 91b357d..653a396 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ default_access_levels: |host|The DNS name or IP address of your LDAP server | e.g. ldap.yourcompany.com | |port|The TCP port of the host that the LDAP server runs under | [default: **389**]| |version|LDAP Version 3 is most popular (only change this if you know what you are doing) | [default: **3**] | -|ssl|Enable SSL for the connection (typically for port 636or 3269) | true \| [default: **false**] | +|ssl|Enable SSL for the connection (typically for port 636 or 3269) | true \| [default: **false**] | |start_tls|Negotiate TLS encryption with the LDAP server (requires all traffic to be encrypted) | true \| [default: **false**] | |opt_referrals|Sets the value of LDAP_OPT_REFERRALS (Set to "off" for Windows 2003 servers) | true \| [default: **false**] | @@ -110,11 +110,13 @@ To be able to know the groups a user is associated with, a valid `group_dn` and ### Storing Grav User -By default the lDAP plugin does not store any local user information. Upon successfully authenticating against the LDAP user, a user is created and is available during the session. However, upon returning, the user must re-authenticate and the LDAP data is retrieved again. +By default the LDAP plugin does not store any local user information. Upon successfully authenticating against the LDAP user, a user is created and is available during the session. However, upon returning, the user must re-authenticate and the LDAP data is retrieved again. If you want to be able to set user data (extra fields, or specific user access) for a particular user, you can enable the `save_grav_user` option, and this will create a local Grav user in the `accounts/` folder. This is a local record of the user and attributes can be set here. > NOTE: Any attribute stored under the `ldap:` key in the user account file will be overwritten by the plugin during the next login. This information is always in sync with latest data in the LDAP server. The same rule goes for the **mapped** fields. So updating `email` in your LDAP directory will ensure the entry in the local Grav user is updated on next login. +> +> Also note that the password will never be stored in the Grav user under `accounts/`. ### Troubleshooting From e7766c61ea9206fc257e06f1e573769bc8b7ea58 Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Fri, 11 May 2018 09:56:45 -0700 Subject: [PATCH 10/23] Added default value in config for access groups --- login-ldap.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/login-ldap.yaml b/login-ldap.yaml index cf720b6..36157ec 100644 --- a/login-ldap.yaml +++ b/login-ldap.yaml @@ -20,3 +20,4 @@ default_access_levels: access: site: login: 'true' + groups: "admin:\r\n admin:\r\n login: true\r\n super: true\r\n site:\r\n login: true\r\nuser:\r\n site:\r\n login: true" From 17ca689c71696bbf92b9298ce267e6f588fa3833 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Fri, 11 May 2018 13:03:24 -0600 Subject: [PATCH 11/23] Lang translations --- blueprints.yaml | 188 +++++++++++++++++++++++----------------------- languages/en.yaml | 38 ++++++++++ 2 files changed, 133 insertions(+), 93 deletions(-) create mode 100644 languages/en.yaml diff --git a/blueprints.yaml b/blueprints.yaml index 25625f4..2c38e75 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -36,40 +36,40 @@ form: tab_1: type: tab - title: Configuration Options + title: PLUGIN_LOGIN_LDAP.CONFIGURATION fields: server_section: type: section - title: Server Configuration + title: PLUGIN_LOGIN_LDAP.SERVER_CONFIGURATION underline: true fields: host: type: text - label: Host + label: PLUGIN_LOGIN_LDAP.HOST size: large - help: Host name of the LDAP server + help: PLUGIN_LOGIN_LDAP.HOST_DESC placeholder: ldap.yourcompany.com validate: required: true port: type: number - label: Port + label: PLUGIN_LOGIN_LDAP.PORT default: 389 size: x-small - help: Port to connect to host + help: PLUGIN_LOGIN_LDAP.PORT_DESC validate: required: true version: type: number - label: Version + label: PLUGIN_LOGIN_LDAP.VERSION default: 3 size: x-small - help: LDAP Version 3 is most popular, only change this if you know what you are doing + help: PLUGIN_LOGIN_LDAP.VERSION_DESC validate: required: true @@ -86,8 +86,8 @@ form: start_tls: type: toggle - label: Negotiate TLS - help: Negotiate TLS encryption with the LDAP server (requires all traffic to be encrypted) + label: PLUGIN_LOGIN_LDAP.NEGOTIATE_TLS + help: PLUGIN_LOGIN_LDAP.NEGOTIATE_TLS_DESC default: 0 highlight: 0 options: @@ -98,8 +98,8 @@ form: opt_referrals: type: toggle - label: Follow Referrals - help: Sets the value of LDAP_OPT_REFERRALS (Set to "off" for Windows 2003 servers) + label: PLUGIN_LOGIN_LDAP.OPT_REFERRALS + help: PLUGIN_LOGIN_LDAP.OPT_REFERRALS_DESC default: 0 highlight: 0 options: @@ -117,123 +117,126 @@ form: user_dn: type: text - label: User DN + label: PLUGIN_LOGIN_LDAP.USER_DN size: large placeholder: uid=[username],dc=company,dc=com - help: String used to authenticate a user, where [username] is directly replaced by user value entered via login + help: PLUGIN_LOGIN_LDAP.USER_DN_DESC validate: required: true search_dn: type: text - label: User Search DN + label: PLUGIN_LOGIN_LDAP.USER_SEARCH_DN size: large placeholder: ou=users,dc=company,dc=com - help: String used to retrieve user data. If not provided, extra LDAP user data will not be stored in Grav user account file + help: PLUGIN_LOGIN_LDAP.USER_SEARCH_DN_DESC group_dn: type: text - label: Group Search DN + label: PLUGIN_LOGIN_LDAP.GROUP_SEARCH_DN size: large placeholder: ou=groups,dc=company,dc=com - help: String used to retrieve user group data. If not provided, extra LDAP group data will not be stored in Grav user account file + help: PLUGIN_LOGIN_LDAP.GROUP_SEARCH_DN_DESC group_query: type: text - label: Group Query + label: PLUGIN_LOGIN_LDAP.GROUP_QUERY size: large placeholder: '(&(cn=*)(memberUid=[username]))' - help: The query used to search Groups. Only change this if you know what you are doing + help: PLUGIN_LOGIN_LDAP.GROUP_QUERY_DESC map_username: type: text - label: Username Mapping + label: PLUGIN_LOGIN_LDAP.USERNAME_MAPPING size: large - help: LDAP Attribute(s) that contains the user's username + help: PLUGIN_LOGIN_LDAP.USERNAME_MAPPING_DESC placeholder: uid map_fullname: type: text - label: User Fullname Mapping + label: PLUGIN_LOGIN_LDAP.FULLNAME_MAPPING size: large - help: LDAP Attribute(s) that contains the user's full name + help: PLUGIN_LOGIN_LDAP.FULLNAME_MAPPING placeholder: givenName lastName map_email: type: text - label: User Email Mapping + label: PLUGIN_LOGIN_LDAP.EMAIL_MAPPING size: large - help: LDAP Attribute that contains the user's email + help: PLUGIN_LOGIN_LDAP.EMAIL_MAPPING_DESC placeholder: mail - advanced_section: - type: section - title: Advanced Configuration - underline: true - - fields: - - save_grav_user: - type: toggle - label: Save Grav user - highlight: 0 - default: 0 - options: - 1: Enabled - 0: Disabled - validate: - type: bool - - store_ldap_data: - type: toggle - label: Store LDAP data - help: If storing a local Grav user, you can also store LDAP data to use in Grav - highlight: 0 - default: 0 - options: - 1: Enabled - 0: Disabled - validate: - type: bool - - default_access_levels.groups: - type: selectize - size: large - label: Default Groups - '@data-options': '\Grav\User\Groups::groups' - classes: fancy - validate: - type: commalist - - default_access_levels.access.site: - type: array - label: Default Site Access - multiple: false - placeholder_key: login - placeholder_value: 'true' - validate: - type: array - required: true - - default_access_levels.access.groups: - classes: frontmatter - type: editor - label: Groups Access Level - autofocus: true - default: "admin:\r\n admin:\r\n login: true\r\n super: true\r\n site:\r\n login: true\r\nuser:\r\n site:\r\n login: true" - codemirror: - mode: 'yaml' - indentUnit: 2 - autofocus: true - indentWithTabs: false - lineNumbers: true - styleActiveLine: true - gutters: ['CodeMirror-lint-markers'] - lint: true - tab_2: type: tab - title: Instructions + title: PLUGIN_LOGIN_LDAP.ADVANCED + fields: + + save_grav_user: + type: toggle + label: PLUGIN_LOGIN_LDAP.SAVE_GRAV_USER + help: PLUGIN_LOGIN_LDAP.SAVE_GRAV_USER_DESC + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + + store_ldap_data: + type: toggle + label: PLUGIN_LOGIN_LDAP.STORE_LDAP_USER + help: PLUGIN_LOGIN_LDAP.STORE_LDAP_USER_DESC + highlight: 0 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool + + default_access_levels.groups: + type: selectize + size: large + label: PLUGIN_LOGIN_LDAP.DEFAULT_ACCESS_LEVELS_GROUPS + '@data-options': '\Grav\User\Groups::groups' + classes: fancy + validate: + type: commalist + + default_access_levels.access.site: + type: array + label: PLUGIN_LOGIN_LDAP.DEFAULT_ACCESS_LEVELS_SITE + multiple: false + placeholder_key: login + placeholder_value: 'true' + validate: + type: array + required: true + + default_access_levels.access.groups: + classes: frontmatter + type: editor + label: PLUGIN_LOGIN_LDAP.DEFAULT_GROUPS_ACCESS_LEVELS + autofocus: true + markdown: true + description: PLUGIN_LOGIN_LDAP.ACCESS_GROUPS_DESC + default: "admin:\r\n admin:\r\n login: true\r\n super: true\r\n site:\r\n login: true\r\nuser:\r\n site:\r\n login: true" + codemirror: + mode: 'yaml' + indentUnit: 2 + autofocus: true + indentWithTabs: false + lineNumbers: true + styleActiveLine: true + gutters: ['CodeMirror-lint-markers'] + lint: true + + + + tab_3: + type: tab + title: PLUGIN_LOGIN_LDAP.INSTRUCTIONS fields: ldap_instructions: @@ -241,4 +244,3 @@ form: markdown: true style: vertical file: 'plugins://login-ldap/README.md' - diff --git a/languages/en.yaml b/languages/en.yaml new file mode 100644 index 0000000..65fc9ec --- /dev/null +++ b/languages/en.yaml @@ -0,0 +1,38 @@ +PLUGIN_LOGIN_LDAP: + ACCESS_GROUPS_DESC: 'NOTE: The first level defines the **LDAP group** to be mapped, subsequent levels define the **Grav Access Levels** to give a user who is assigned to that group.' + ADVANCED: 'Advanced' + CONFIGURATION: 'Configuration' + DEFAULT_ACCESS_LEVELS_GROUPS: 'Default Groups' + DEFAULT_ACCESS_LEVELS_SITE: 'Default Site Access' + DEFAULT_GROUPS_ACCESS_LEVELS: 'Groups Access Level' + GROUP_SEARCH_DN: 'Group Search DN' + GROUP_SEARCH_DN_DESC: 'String used to retrieve user group data. If not provided, extra LDAP group data will not be stored in Grav user account file' + GROUP_QUERY: 'Group Query' + GROUP_QUERY_DESC: 'The query used to search Groups. Only change this if you know what you are doing' + HOST: 'Host' + HOST_DESC: 'Host name of the LDAP server' + INSTRUCTIONS: 'Instructions' + NEGOTIATE_TLS: 'Negotiate TLS' + NEGOTIATE_TLS_DESC: 'Negotiate TLS encryption with the LDAP server (requires all traffic to be encrypted)' + OPT_REFERRALS: 'Follow Referrals' + OPT_REFERRALS_DESC: 'Sets the value of LDAP_OPT_REFERRALS (Set to "off" for Windows 2003 servers)' + PORT: 'Port' + PORT_DESC: 'Port to connect to host' + SAVE_GRAV_USER: 'Save Grav user' + SAVE_GRAV_USER_DESC: 'Saves the logged in user as a local Grav account' + STORE_LDAP_USER: 'Store LDAP data' + STORE_LDAP_USER_DESC: 'If storing a local Grav user, you can also store LDAP data to use in Grav' + SERVER_CONFIGURATION: 'Server Configuration' + USE_SSL: 'Use SSL' + USER_DN: 'User DN' + USER_DN_DESC: 'String used to authenticate a user, where [username] is directly replaced by user value entered via login' + USERNAME_MAPPING: 'Username Mapping' + USERNAME_MAPPING_DESC: 'LDAP Attribute(s) that contains the user''s username' + FULLNAME_MAPPING: 'User Fullname Mapping' + FULLNAME_MAPPING_DESC: 'LDAP Attribute(s) that contains the user''s full name' + EMAIL_MAPPING: 'User Email Mapping' + EMAIL_MAPPING_DESC: 'LDAP Attribute that contains the user''s email' + USER_SEARCH_DN: 'User Search DN' + USER_SEARCH_DN_DESC: 'String used to retrieve user data. If not provided, extra LDAP user data will not be stored in Grav user account file' + VERSION: 'Version' + VERSION_DESC: 'LDAP Version 3 is most popular, only change this if you know what you are doing' From b9820baa5083df3f21f2963e222843aa090d24e3 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Fri, 11 May 2018 13:43:09 -0600 Subject: [PATCH 12/23] updated README.md --- README.md | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 653a396..29815bd 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,15 @@ default_access_levels: groups: ldap_users access: site: - login: 'true' + login: true + groups: + admin: + admin: + login: true + super: true + user: + site: + login: true ``` ### Server Settings @@ -90,7 +98,9 @@ default_access_levels: |save_grav_user|Store the grav user account as a local YAML account | true \| [default: **false**] | |store_ldap_data|If storing a local Grav user, you can also store LDAP data so its available in Grav| true \| [default: **false**] | |default_access_levels.groups|Set a default group for all users logging in via LDAP [OPTIONAL] | e.g. `ldap_users` | -|default_access_levels.access.site|The default access to assign to users logging in via LDAP | e.g. `site: [login: 'true']` | +|default_access_levels.access.site|Set the default **site access** for all users logging in via LDAP (used if no `access.groups` mapping applies) | e.g. `[login: 'true']` | +|default_access_levels.access.groups|The default **access to assign** to users logging in based on **LDAP group membership**| e.g. `user: [site: [login: 'true']]` | + > Note that if you use the admin plugin, a file with your configuration will be saved in the `user/config/plugins/login-ldap.yaml`. @@ -102,12 +112,42 @@ For the most basic of authentication, only the `user_dn` is required. This uses #### LDAP User Data -In order to obtain data about the user a valid `search_dn` is required. This will search the LDAP directory at the level indicated in the DN and search for a userid with the `username` provided. the `map_username` field is used in this LDAP search query, so it's important that the `map_username` field is one that properly maps the `username` provided during login to the LDAP user entry. +In order to obtain data about the user a valid `search_dn` is required. This will search the LDAP directory at the level indicated in the DN and search for a userid with the `username` provided. the `map_username` field is used in this LDAP search query, so it's important that the `map_username` field is one that properly maps the `username` provided during login to the LDAP user entry. #### LDAP Group Data To be able to know the groups a user is associated with, a valid `group_dn` and `group_query` is required. Any invalid information will throw an exception stating that the search could not complete. + +### LDAP Group to Grav Access Mapping + +The LDAP plugin provides a flexible way to map your LDAP users into Grav. + +> For Groups and Access mapping to work properly a valid `search_dn`, `query_dn` and `group_query` is required. + +The default configuration for `default_access_levels.access.groups` looks like: + +```yaml +admin: + admin: + login: true + super: true + site: + login: true +user: + site: + login: true +``` + +How this works is the two top-level YAML keys (`admin` and `user`) are LDAP groups that if a user is a member of, will assign the subsequent values as the Grav user access. + +In order for a front-end user to be able to log into a Grav site the minimum of `site: [login: true]` is required. So as long as the user is a member of the LDAP group `user`, they will be assigned the necessary access to log in to the front of the site, and nothing more. You can of course configure this with any access settings you wish to provide. + +For a user to have access to the admin, they must have at least `admin: [login: true]` access, and to be a full administrator with access to everything, they also need to have `admin: [super: true]` access. In the default configuration, users with LDAP group `admin` will be assigned this access. This will probably need to be modified for your directory configuration. + +> NOTE: See the [Groups and Permissions Documentation](https://learn.getgrav.org/advanced/groups-and-permissions?target=_blank) for more information about how Grav permissions work in conjunction with access levels and groups. + + ### Storing Grav User By default the LDAP plugin does not store any local user information. Upon successfully authenticating against the LDAP user, a user is created and is available during the session. However, upon returning, the user must re-authenticate and the LDAP data is retrieved again. From d4dad1014a11b02293265bf909ec248c141191c8 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Wed, 16 May 2018 09:25:42 -0600 Subject: [PATCH 13/23] group should be array by default --- README.md | 3 ++- login-ldap.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 29815bd..c0437be 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,8 @@ map_email: mail save_grav_user: false store_ldap_data: false default_access_levels: - groups: ldap_users + groups: + - ldap_users access: site: login: true diff --git a/login-ldap.yaml b/login-ldap.yaml index 36157ec..c47fee8 100644 --- a/login-ldap.yaml +++ b/login-ldap.yaml @@ -16,7 +16,8 @@ map_email: mail save_grav_user: false store_ldap_data: false default_access_levels: - groups: ldap_users + groups: + - ldap_users access: site: login: 'true' From b92c654aa149647be7d615af72539e54b31f8157 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Wed, 16 May 2018 09:26:04 -0600 Subject: [PATCH 14/23] =?UTF-8?q?add=20a=20=E2=80=98provider=E2=80=99=20at?= =?UTF-8?q?tribute=20to=20help=20identify=20auth=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- login-ldap.php | 1 + 1 file changed, 1 insertion(+) diff --git a/login-ldap.php b/login-ldap.php index b871b4b..0afe4c8 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -139,6 +139,7 @@ class LoginLDAPPlugin extends Plugin $userdata['login'] = $this->getLDAPMappedItem($map_username, $ldap_data); $userdata['fullname'] = $this->getLDAPMappedItem($map_fullname, $ldap_data); $userdata['email'] = $this->getLDAPMappedItem($map_email, $ldap_data); + $userdata['provider'] = 'ldap'; // Get LDAP Data if required if ($this->config->get('plugins.login-ldap.store_ldap_data', false)) { From ffe5f6a5a00fdefe6aad59dca214e9cb9f5add2e Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Wed, 16 May 2018 12:24:08 -0700 Subject: [PATCH 15/23] Implemented support for LDAP Blacklist Fields --- README.md | 4 ++++ blueprints.yaml | 9 +++++++++ languages/en.yaml | 3 +++ login-ldap.php | 9 ++++++++- 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c0437be..131a5c4 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,10 @@ If you want to be able to set user data (extra fields, or specific user access) > > Also note that the password will never be stored in the Grav user under `accounts/`. +### Blacklist LDAP Fields + +With the Blacklist Fields you have the option of ignoring fields. This is useful for skipping users sensitive data or fields that are stored as media. For example phone numbers, home addresses or images, videos, etc. + ### Troubleshooting If a user is simply unable to authenticate against the LDAP server, an entry will be logged into the Grav log (`logs/grav.log`) file with the attempted `dn`. This can be used to ensure the `user_dn` entry is correct and can be tested against any other LDAP login system. diff --git a/blueprints.yaml b/blueprints.yaml index 2c38e75..f6377c6 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -204,6 +204,15 @@ form: validate: type: commalist + blacklist_ldap_fields: + type: array + type: array + value_only: true + label: PLUGIN_LOGIN_LDAP.BLACKLIST_FIELDS + help: PLUGIN_LOGIN_LDAP.BLACKLIST_FIELDS_HELP + placeholder_key: key + placeholder_value: PLUGIN_LOGIN_LDAP.BLACKLIST_FIELDS_PLACEHOLDER + default_access_levels.access.site: type: array label: PLUGIN_LOGIN_LDAP.DEFAULT_ACCESS_LEVELS_SITE diff --git a/languages/en.yaml b/languages/en.yaml index 65fc9ec..8366332 100644 --- a/languages/en.yaml +++ b/languages/en.yaml @@ -36,3 +36,6 @@ PLUGIN_LOGIN_LDAP: USER_SEARCH_DN_DESC: 'String used to retrieve user data. If not provided, extra LDAP user data will not be stored in Grav user account file' VERSION: 'Version' VERSION_DESC: 'LDAP Version 3 is most popular, only change this if you know what you are doing' + BLACKLIST_FIELDS: 'Blacklist Fields' + BLACKLIST_FIELDS_HELP: 'A list of LDAP fields to be skipped and ignored' + BLACKLIST_FIELDS_PLACEHOLDER: 'Field (ie, jpegPhoto, homePostalAddress)' \ No newline at end of file diff --git a/login-ldap.php b/login-ldap.php index 0afe4c8..9d0f5fe 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -84,6 +84,7 @@ class LoginLDAPPlugin extends Plugin $ssl = $this->config->get('plugins.login-ldap.ssl'); $start_tls = $this->config->get('plugins.login-ldap.start_tls'); $opt_referrals = $this->config->get('plugins.login-ldap.opt_referrals'); + $blacklist = $this->config->get('plugins.login-ldap.blacklist_ldap_fields', []); if (is_null($host)) { throw new ConnectionException('FATAL: LDAP host entry missing in plugin configuration...'); @@ -128,7 +129,6 @@ class LoginLDAPPlugin extends Plugin // If search_dn is set we can try to get information from LDAP if ($search_dn) { - $query = $ldap->query($search_dn, $map_username .'='. $credentials['username']); $results = $query->execute()->toArray(); @@ -149,6 +149,13 @@ class LoginLDAPPlugin extends Plugin unset($userdata['ldap']['userPassword']); } + // Remove blacklisted fields + foreach ($blacklist as $fieldName) { + if (isset($userdata['ldap'][$fieldName])) { + unset($userdata['ldap'][$fieldName]); + } + } + // Get Groups if group_dn if set if ($group_dn) { // retrieves all extra groups for user From af5f5888caa6ffe58c2f1b2663fa231c6c93bc8f Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Wed, 16 May 2018 12:24:25 -0700 Subject: [PATCH 16/23] Fixed issue with merge of access levels --- login-ldap.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/login-ldap.php b/login-ldap.php index 9d0f5fe..a1d4a2b 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -3,6 +3,7 @@ namespace Grav\Plugin; use Grav\Common\Plugin; use Grav\Common\User\User; +use Grav\Common\Utils; use Grav\Plugin\Login\Events\UserLoginEvent; use Grav\Plugin\Login\Login; use Symfony\Component\Ldap\Ldap; @@ -209,7 +210,8 @@ class LoginLDAPPlugin extends Plugin $groups_access = Yaml::parse($admin_access); foreach ($groups_access as $key => $group_access) { if (in_array($key, $user_groups)) { - $grav_user->merge(['access' => $group_access]); + $access_levels = Utils::arrayMergeRecursiveUnique($grav_user->access, $group_access); + $grav_user->merge(['access' => $access_levels]); } } } From 47dcea06c7b036f14afd53a12bb3076120a99a01 Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Wed, 16 May 2018 12:24:50 -0700 Subject: [PATCH 17/23] Force loading the lowercase version of a user filename. --- login-ldap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/login-ldap.php b/login-ldap.php index a1d4a2b..7dd6f43 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -121,7 +121,7 @@ class LoginLDAPPlugin extends Plugin $ldap->bind($username, $credentials['password']); // Create Grav User - $grav_user = User::load($username); + $grav_user = User::load(strtolower($username)); // Set defaults with only thing we know... username provided $grav_user['login'] = $credentials['username']; From a6def9336ff49cea38abd429d7337551a5015250 Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Wed, 16 May 2018 12:33:46 -0700 Subject: [PATCH 18/23] Added example LDAP Data page --- README.md | 2 ++ example/default.md | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 example/default.md diff --git a/README.md b/README.md index 131a5c4..e3cab66 100644 --- a/README.md +++ b/README.md @@ -171,3 +171,5 @@ If either the `user_dn`, `search_dn`, `group_dn` or `group_query` are incorrect If you expect `fullname`, or `email` to be stored in the Grav user object, but they are not appearing, it's probably a problem with your field mappings. Double check with your LDAP administrator that these are the correct mappings. +Under the `example` folder you can find a `default.md` page that you can use to see the data collected during LDAP authentication. It's an useful way for configuring the plugin as well as tweaking the Blacklist. + diff --git a/example/default.md b/example/default.md new file mode 100644 index 0000000..dbeb10d --- /dev/null +++ b/example/default.md @@ -0,0 +1,21 @@ +--- +title: LDAP Data +cache_enabled: false +process: + twig: true +access: + site.login: true +--- + +# This is a secure page... +## Welcome {{ grav.user.fullname }} + +User `{{ grav.user.username }}` {{ grav.user.exists ? '**has** a' : 'does **not** have' }} local account... + +* username: `{{ grav.user.username }}` +* email: `{{ grav.user.email }}` +* login: `{{ grav.user.login }}` +* provider: `{{ grav.user.provider }}` +* groups: {{ vardump(grav.user.groups) }} +* access: {{ vardump(grav.user.access) }} +* ldap: {{ vardump(grav.user.ldap) }} From de19fcfe6587f33762f67077aefbe8ece8db8b9d Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Wed, 16 May 2018 16:03:49 -0600 Subject: [PATCH 19/23] detect and log invalid uid mapping throwing attribute error --- login-ldap.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/login-ldap.php b/login-ldap.php index 7dd6f43..46bcc6c 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -130,11 +130,18 @@ class LoginLDAPPlugin extends Plugin // If search_dn is set we can try to get information from LDAP if ($search_dn) { - $query = $ldap->query($search_dn, $map_username .'='. $credentials['username']); + $query_string = $map_username .'='. $credentials['username']; + $query = $ldap->query($search_dn, $query_string); $results = $query->execute()->toArray(); // Get LDAP Data - $ldap_data = array_shift($results)->getAttributes(); + if (empty($results)) { + $this->grav['log']->error('plugin.login-ldap: [401] user search for "' . $query_string . '" returned no user data'); + $ldap_data =[]; + } else { + $ldap_data = array_shift($results)->getAttributes(); + } + $userdata = []; $userdata['login'] = $this->getLDAPMappedItem($map_username, $ldap_data); From 52117ef7933c55eb89ec8f44b96cb60e5df96842 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Wed, 16 May 2018 18:19:41 -0600 Subject: [PATCH 20/23] updated version --- blueprints.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints.yaml b/blueprints.yaml index f6377c6..0158e46 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -1,5 +1,5 @@ name: Login LDAP -version: 0.1.0 +version: 0.1.1 description: Allows for Grav user authentication against an LDAP Server such as OpenLDAP or ActiveDirectory icon: user-circle-o author: From 98205c1167749ac48a78a49e603038948b986e43 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Thu, 17 May 2018 15:28:32 -0600 Subject: [PATCH 21/23] Added group_indentifier option --- README.md | 50 ++++++++++++++++++++++++++++++++--------------- blueprints.yaml | 9 ++++++++- languages/en.yaml | 2 ++ login-ldap.php | 25 ++++++++++++------------ login-ldap.yaml | 1 + 5 files changed, 58 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index e3cab66..f85a5a9 100644 --- a/README.md +++ b/README.md @@ -46,10 +46,10 @@ user_dn: 'uid=[username],dc=company,dc=com' search_dn: group_dn: group_query: '(&(cn=*)(memberUid=[username]))' +group_indentifier: cn map_username: uid map_fullname: givenName lastName map_email: mail - save_grav_user: false store_ldap_data: false default_access_levels: @@ -72,32 +72,33 @@ default_access_levels: |Key |Description | Values | |:---------------------|:---------------------------|:-------| -|enabled|Enables the plugin | [default: **true**] \| false| -|host|The DNS name or IP address of your LDAP server | e.g. ldap.yourcompany.com | -|port|The TCP port of the host that the LDAP server runs under | [default: **389**]| -|version|LDAP Version 3 is most popular (only change this if you know what you are doing) | [default: **3**] | -|ssl|Enable SSL for the connection (typically for port 636 or 3269) | true \| [default: **false**] | -|start_tls|Negotiate TLS encryption with the LDAP server (requires all traffic to be encrypted) | true \| [default: **false**] | -|opt_referrals|Sets the value of LDAP_OPT_REFERRALS (Set to "off" for Windows 2003 servers) | true \| [default: **false**] | +|enabled|Enables the plugin | [default: `true`] \| `false`| +|host|The DNS name or IP address of your LDAP server | e.g. `ldap.yourcompany.com` | +|port|The TCP port of the host that the LDAP server runs under | [default: `389`]| +|version|LDAP Version 3 is most popular (only change this if you know what you are doing) | [default: `3`] | +|ssl|Enable SSL for the connection (typically for port 636 or 3269) | `true` \| [default: `false`] | +|start_tls|Negotiate TLS encryption with the LDAP server (requires all traffic to be encrypted) | `true` \| [default: `false`] | +|opt_referrals|Sets the value of LDAP_OPT_REFERRALS (Set to "off" for Windows 2003 servers) | `true` \| [default: `false`] | ### LDAP Configuration |Key |Description | Values | |:---------------------|:---------------------------|:-------| -|user_dn|DN String used to authenticate a user, where `[username]` is replaced by username value entered via login | e.g. `uid=[username],dc=company,dc=com` | +|user_dn|DN String used to authenticate a user, where `[username]` is replaced by username value entered via login | [default: `uid=[username],dc=company,dc=com`] | |search_dn|DN String used to retrieve user data. If not provided, extra LDAP user data will not be stored in Grav user account file [OPTIONAL]| e.g. `ou=users,dc=company,dc=com` | |group_dn|DN String used to retrieve user group data. If not provided, extra LDAP group data will not be stored in Grav user account file [OPTIONAL] | e.g. `ou=groups,dc=company,dc=com` | -|group_query|The query used to search Groups. Only change this if you know what you are doing| e.g. `(&(cn=*)(memberUid=[username]))`| -|map_username|LDAP Attribute(s) that contains the user's username | [default: **uid**] | -|map_fullname|LDAP Attribute(s) that contains the user's full name | [default: **givenName lastName**] | -|map_email|LDAP Attribute(s) that contains the user's email address | [default: **mail**] | +|group_query|The query used to search Groups. Only change this if you know what you are doing| [default: `(&(cn=*)(memberUid=[username]))`] | +|group_indentifier|The Group identifier that will come back in the response, this is directly related to group query.| [default: `cn`] | +|map_username|LDAP Attribute(s) that contains the user's username | [default: `uid`] | +|map_fullname|LDAP Attribute(s) that contains the user's full name | [default: `givenName lastName`] | +|map_email|LDAP Attribute(s) that contains the user's email address | [default: `mail`] | ### Advanced Configuration |Key |Description | Values | |:---------------------|:---------------------------|:-------| -|save_grav_user|Store the grav user account as a local YAML account | true \| [default: **false**] | -|store_ldap_data|If storing a local Grav user, you can also store LDAP data so its available in Grav| true \| [default: **false**] | +|save_grav_user|Store the grav user account as a local YAML account | true \| [default: `false`] | +|store_ldap_data|If storing a local Grav user, you can also store LDAP data so its available in Grav| true \| [default: `false`] | |default_access_levels.groups|Set a default group for all users logging in via LDAP [OPTIONAL] | e.g. `ldap_users` | |default_access_levels.access.site|Set the default **site access** for all users logging in via LDAP (used if no `access.groups` mapping applies) | e.g. `[login: 'true']` | |default_access_levels.access.groups|The default **access to assign** to users logging in based on **LDAP group membership**| e.g. `user: [site: [login: 'true']]` | @@ -171,5 +172,22 @@ If either the `user_dn`, `search_dn`, `group_dn` or `group_query` are incorrect If you expect `fullname`, or `email` to be stored in the Grav user object, but they are not appearing, it's probably a problem with your field mappings. Double check with your LDAP administrator that these are the correct mappings. -Under the `example` folder you can find a `default.md` page that you can use to see the data collected during LDAP authentication. It's an useful way for configuring the plugin as well as tweaking the Blacklist. +To get a quick state of your LDAP configuration, you can simply dump out the Grav user on a temporary _secure_ page: + +```markdown +--- +title: LDAP Test +cache_enabled: false +process: + twig: true +access: + site.login: true +--- + +# Grav User + +{{ vardump(grav.user) }} +``` + +For a more detailed example, you can look in the `example.` folder of this plugin, where you can find a `default.md` page that you can use to see the data collected during LDAP authentication. It's an useful way for configuring the plugin as well as tweaking the Blacklist. diff --git a/blueprints.yaml b/blueprints.yaml index 0158e46..4b6b0f6 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -1,5 +1,5 @@ name: Login LDAP -version: 0.1.1 +version: 0.1.2 description: Allows for Grav user authentication against an LDAP Server such as OpenLDAP or ActiveDirectory icon: user-circle-o author: @@ -145,6 +145,13 @@ form: placeholder: '(&(cn=*)(memberUid=[username]))' help: PLUGIN_LOGIN_LDAP.GROUP_QUERY_DESC + group_indentifier: + type: text + label: PLUGIN_LOGIN_LDAP.GROUP_IDENTIFIER + size: large + placeholder: 'cn' + help: PLUGIN_LOGIN_LDAP.GROUP_IDENTIFIER_DESC + map_username: type: text label: PLUGIN_LOGIN_LDAP.USERNAME_MAPPING diff --git a/languages/en.yaml b/languages/en.yaml index 8366332..592d13d 100644 --- a/languages/en.yaml +++ b/languages/en.yaml @@ -9,6 +9,8 @@ PLUGIN_LOGIN_LDAP: GROUP_SEARCH_DN_DESC: 'String used to retrieve user group data. If not provided, extra LDAP group data will not be stored in Grav user account file' GROUP_QUERY: 'Group Query' GROUP_QUERY_DESC: 'The query used to search Groups. Only change this if you know what you are doing' + GROUP_IDENTIFIER: 'Group Identifier' + GROUP_IDENTIFIER_DESC: 'The Group identifier that will come back in the response, this is directly related to group query.' HOST: 'Host' HOST_DESC: 'Host name of the LDAP server' INSTRUCTIONS: 'Instructions' diff --git a/login-ldap.php b/login-ldap.php index 46bcc6c..6c08b3e 100644 --- a/login-ldap.php +++ b/login-ldap.php @@ -71,21 +71,22 @@ class LoginLDAPPlugin extends Plugin $credentials = $event->getCredentials(); // Get Proper username - $user_dn = $this->config->get('plugins.login-ldap.user_dn'); - $search_dn = $this->config->get('plugins.login-ldap.search_dn'); - $group_dn = $this->config->get('plugins.login-ldap.group_dn'); - $group_query = $this->config->get('plugins.login-ldap.group_query'); + $user_dn = $this->config->get('plugins.login-ldap.user_dn'); + $search_dn = $this->config->get('plugins.login-ldap.search_dn'); + $group_dn = $this->config->get('plugins.login-ldap.group_dn'); + $group_query = $this->config->get('plugins.login-ldap.group_query'); + $group_indentifier = $this->config->get('plugins.login-ldap.group_indentifier'); $username = str_replace('[username]', $credentials['username'], $user_dn); // Get Host info - $host = $this->config->get('plugins.login-ldap.host'); - $port = $this->config->get('plugins.login-ldap.port'); - $version = $this->config->get('plugins.login-ldap.version'); - $ssl = $this->config->get('plugins.login-ldap.ssl'); - $start_tls = $this->config->get('plugins.login-ldap.start_tls'); - $opt_referrals = $this->config->get('plugins.login-ldap.opt_referrals'); - $blacklist = $this->config->get('plugins.login-ldap.blacklist_ldap_fields', []); + $host = $this->config->get('plugins.login-ldap.host'); + $port = $this->config->get('plugins.login-ldap.port'); + $version = $this->config->get('plugins.login-ldap.version'); + $ssl = $this->config->get('plugins.login-ldap.ssl'); + $start_tls = $this->config->get('plugins.login-ldap.start_tls'); + $opt_referrals = $this->config->get('plugins.login-ldap.opt_referrals'); + $blacklist = $this->config->get('plugins.login-ldap.blacklist_ldap_fields', []); if (is_null($host)) { throw new ConnectionException('FATAL: LDAP host entry missing in plugin configuration...'); @@ -177,7 +178,7 @@ class LoginLDAPPlugin extends Plugin foreach ($groups as $group) { $attributes = $group->getAttributes(); - $user_group = array_shift($attributes['cn']); + $user_group = array_shift($attributes[$group_indentifier]); $user_groups[] = $user_group; if ($this->config->get('plugins.login-ldap.store_ldap_data', false)) { diff --git a/login-ldap.yaml b/login-ldap.yaml index c47fee8..421f4aa 100644 --- a/login-ldap.yaml +++ b/login-ldap.yaml @@ -9,6 +9,7 @@ user_dn: 'uid=[username],dc=company,dc=com' search_dn: group_dn: group_query: '(&(cn=*)(memberUid=[username]))' +group_indentifier: cn map_username: uid map_fullname: givenName lastName map_email: mail From 642b50160590f4efb551bae9be3dab89acdde56a Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Thu, 17 May 2018 17:26:59 -0600 Subject: [PATCH 22/23] Fix a lang string --- languages/en.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/en.yaml b/languages/en.yaml index 592d13d..7fd369a 100644 --- a/languages/en.yaml +++ b/languages/en.yaml @@ -23,7 +23,7 @@ PLUGIN_LOGIN_LDAP: SAVE_GRAV_USER: 'Save Grav user' SAVE_GRAV_USER_DESC: 'Saves the logged in user as a local Grav account' STORE_LDAP_USER: 'Store LDAP data' - STORE_LDAP_USER_DESC: 'If storing a local Grav user, you can also store LDAP data to use in Grav' + STORE_LDAP_USER_DESC: 'You can also store LDAP data on the logged in user object to use in Grav' SERVER_CONFIGURATION: 'Server Configuration' USE_SSL: 'Use SSL' USER_DN: 'User DN' From d8dca931a2897f63ce6bb62abd53f67021515043 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Fri, 18 May 2018 16:49:25 -0600 Subject: [PATCH 23/23] prepare for release --- CHANGELOG.md | 6 +++--- blueprints.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6d4c29..248ebce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -# v0.1.0 -## 05/07/2018 +# v1.0.0 +## 05/18/2018 1. [](#new) - * ChangeLog started... + * Plugin released... diff --git a/blueprints.yaml b/blueprints.yaml index 4b6b0f6..9d7bd0d 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -1,5 +1,5 @@ name: Login LDAP -version: 0.1.2 +version: 1.0.0 description: Allows for Grav user authentication against an LDAP Server such as OpenLDAP or ActiveDirectory icon: user-circle-o author: