Merge branch 'release/1.0.0'

This commit is contained in:
Andy Miller
2018-05-18 16:49:33 -06:00
7 changed files with 627 additions and 145 deletions

View File

@@ -1,5 +1,5 @@
# v0.1.0 # v1.0.0
## 05/07/2018 ## 05/18/2018
1. [](#new) 1. [](#new)
* ChangeLog started... * Plugin released...

179
README.md
View File

@@ -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 for [Grav CMS](http://github.com/getgrav/grav) allows user authentication against an LDAP Server.
The **Login Ldap** Plugin is for [Grav CMS](http://github.com/getgrav/grav). Allows 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.
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) ### 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 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 ### Manual Installation
@@ -23,34 +21,173 @@ To install this plugin, just download the zip version of this repository and unz
You should now have all the plugin files under You should now have all the plugin files under
/your/site/grav/user/plugins/login-ldap /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 ```yaml
enabled: true 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:
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:
groups:
- ldap_users
access:
site:
login: true
groups:
admin:
admin:
login: true
super: true
user:
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
## Usage |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`] |
**Describe how to use the plugin.** ### LDAP Configuration
## Credits |Key |Description | Values |
|:---------------------|:---------------------------|:-------|
|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| [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`] |
**Did you incorporate third-party code? Want to thank somebody?** ### Advanced Configuration
## To Do |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|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']]` |
- [ ] Future plans, if any
> 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
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.
### 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.
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/`.
### 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.
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.
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.

View File

@@ -1,17 +1,20 @@
name: Login Ldap name: Login LDAP
version: 0.1.0 version: 1.0.0
description: Allows authentication against an LDAP Server description: Allows for Grav user authentication against an LDAP Server such as OpenLDAP or ActiveDirectory
icon: plug icon: user-circle-o
author: author:
name: Trilby Media name: Trilby Media
email: hello@trilby.media email: hello@trilby.media
homepage: https://github.com/trilbymedia/grav-plugin-login-ldap homepage: https://github.com/trilbymedia/grav-plugin-login-ldap
demo: http://demo.yoursite.com 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 bugs: https://github.com/trilbymedia/grav-plugin-login-ldap/issues
docs: https://github.com/trilbymedia/grav-plugin-login-ldap/blob/develop/README.md docs: https://github.com/trilbymedia/grav-plugin-login-ldap/blob/develop/README.md
license: MIT license: MIT
dependencies:
- { name: login, version: '>=2.6.3' }
form: form:
validation: strict validation: strict
fields: fields:
@@ -26,110 +29,234 @@ form:
validate: validate:
type: bool type: bool
server_section: ldap_tabs:
type: section type: tabs
title: LDAP Server
fields: fields:
host: tab_1:
type: text type: tab
label: Host title: PLUGIN_LOGIN_LDAP.CONFIGURATION
help: Host name of the LDAP server fields:
validate:
required: true
port: server_section:
type: number type: section
label: Port title: PLUGIN_LOGIN_LDAP.SERVER_CONFIGURATION
default: 389 underline: true
help: Port to connect to host
validate:
required: true
version: fields:
type: number
label: Version
default: 3
help: LDAP Version 3 is most popular, only change this if you know what you are doing
validate:
required: true
ssl: host:
type: toggle type: text
label: Use SSL label: PLUGIN_LOGIN_LDAP.HOST
default: 0 size: large
highlight: 0 help: PLUGIN_LOGIN_LDAP.HOST_DESC
options: placeholder: ldap.yourcompany.com
1: PLUGIN_ADMIN.YES validate:
0: PLUGIN_ADMIN.NO required: true
validate:
type: bool
start_tls: port:
type: toggle type: number
label: Negotiate TLS label: PLUGIN_LOGIN_LDAP.PORT
help: Negotiate TLS encryption with the LDAP server (requires all traffic to be encrypted) default: 389
default: 0 size: x-small
highlight: 0 help: PLUGIN_LOGIN_LDAP.PORT_DESC
options: validate:
1: PLUGIN_ADMIN.YES required: true
0: PLUGIN_ADMIN.NO
validate:
type: bool
opt_referrals: version:
type: toggle type: number
label: Follow Referrals label: PLUGIN_LOGIN_LDAP.VERSION
help: Sets the value of LDAP_OPT_REFERRALS (Set to "off" for Windows 2003 servers) default: 3
default: 0 size: x-small
highlight: 0 help: PLUGIN_LOGIN_LDAP.VERSION_DESC
options: validate:
1: PLUGIN_ADMIN.YES required: true
0: PLUGIN_ADMIN.NO
validate:
type: bool
config_section: ssl:
type: section type: toggle
title: LDAP Configuration 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: PLUGIN_LOGIN_LDAP.NEGOTIATE_TLS
help: PLUGIN_LOGIN_LDAP.NEGOTIATE_TLS_DESC
default: 0
highlight: 0
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
user_dn: opt_referrals:
type: text type: toggle
label: User Search DN label: PLUGIN_LOGIN_LDAP.OPT_REFERRALS
placeholder: uid=[username],dc=company,dc=com help: PLUGIN_LOGIN_LDAP.OPT_REFERRALS_DESC
help: String used to authenticate a user, where [username] is directly replaced by user value entered via login default: 0
validate: highlight: 0
required: true options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
data_dn: config_section:
type: text type: section
label: User Data DN title: LDAP Configuration
placeholder: dc=company,dc=com underline: true
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: fields:
type: text
label: Username Mapping
help: LDAP Attribute(s) that contains the user's username
placeholder: uid
validate:
required: true
map_fullname: user_dn:
type: text type: text
label: User Fullname Mapping label: PLUGIN_LOGIN_LDAP.USER_DN
help: LDAP Attribute(s) that contains the user's full name size: large
placeholder: givenName lastName placeholder: uid=[username],dc=company,dc=com
validate: help: PLUGIN_LOGIN_LDAP.USER_DN_DESC
required: true validate:
required: true
map_email: search_dn:
type: text type: text
label: User Email Mapping label: PLUGIN_LOGIN_LDAP.USER_SEARCH_DN
help: LDAP Attribute that contains the user's email size: large
placeholder: mail placeholder: ou=users,dc=company,dc=com
validate: help: PLUGIN_LOGIN_LDAP.USER_SEARCH_DN_DESC
required: true
group_dn:
type: text
label: PLUGIN_LOGIN_LDAP.GROUP_SEARCH_DN
size: large
placeholder: ou=groups,dc=company,dc=com
help: PLUGIN_LOGIN_LDAP.GROUP_SEARCH_DN_DESC
group_query:
type: text
label: PLUGIN_LOGIN_LDAP.GROUP_QUERY
size: large
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
size: large
help: PLUGIN_LOGIN_LDAP.USERNAME_MAPPING_DESC
placeholder: uid
map_fullname:
type: text
label: PLUGIN_LOGIN_LDAP.FULLNAME_MAPPING
size: large
help: PLUGIN_LOGIN_LDAP.FULLNAME_MAPPING
placeholder: givenName lastName
map_email:
type: text
label: PLUGIN_LOGIN_LDAP.EMAIL_MAPPING
size: large
help: PLUGIN_LOGIN_LDAP.EMAIL_MAPPING_DESC
placeholder: mail
tab_2:
type: tab
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
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
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:
type: display
markdown: true
style: vertical
file: 'plugins://login-ldap/README.md'

21
example/default.md Normal file
View File

@@ -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) }}

43
languages/en.yaml Normal file
View File

@@ -0,0 +1,43 @@
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'
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'
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: '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'
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'
BLACKLIST_FIELDS: 'Blacklist Fields'
BLACKLIST_FIELDS_HELP: 'A list of LDAP fields to be skipped and ignored'
BLACKLIST_FIELDS_PLACEHOLDER: 'Field (ie, jpegPhoto, homePostalAddress)'

View File

@@ -3,10 +3,12 @@ namespace Grav\Plugin;
use Grav\Common\Plugin; use Grav\Common\Plugin;
use Grav\Common\User\User; use Grav\Common\User\User;
use Grav\Common\Utils;
use Grav\Plugin\Login\Events\UserLoginEvent; use Grav\Plugin\Login\Events\UserLoginEvent;
use Grav\Plugin\Login\Login; use Grav\Plugin\Login\Login;
use Symfony\Component\Ldap\Ldap; use Symfony\Component\Ldap\Ldap;
use Symfony\Component\Ldap\Exception\ConnectionException; use Symfony\Component\Ldap\Exception\ConnectionException;
use Symfony\Component\Yaml\Yaml;
/** /**
* Class LoginLDAPPlugin * Class LoginLDAPPlugin
@@ -66,44 +68,166 @@ class LoginLDAPPlugin extends Plugin
public function userLoginAuthenticate(UserLoginEvent $event) public function userLoginAuthenticate(UserLoginEvent $event)
{ {
$username = $event->getUser();
$credentials = $event->getCredentials(); $credentials = $event->getCredentials();
// This gets fired for user authentication. // Get Proper username
$username="cn=amiller,ou=users,dc=trilbymedia,dc=com"; $user_dn = $this->config->get('plugins.login-ldap.user_dn');
$credentials="gom8Jabar"; $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', []);
if (is_null($host)) {
throw new ConnectionException('FATAL: LDAP host entry missing in plugin configuration...');
}
// Set Encryption
if ((bool) $ssl) {
$encryption = 'ssl';
} elseif ((bool) $start_tls) {
$encryption = 'tls';
} else {
$encryption = 'none';
}
try { try {
/** @var Ldap $ldap */
$ldap = Ldap::create('ext_ldap', array( $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); // Try to login via LDAP
$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']);
// Create Grav User // 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'];
$grav_user['fullname'] = $credentials['username'];
$user_groups = [];
// If search_dn is set we can try to get information from LDAP
if ($search_dn) {
$query_string = $map_username .'='. $credentials['username'];
$query = $ldap->query($search_dn, $query_string);
$results = $query->execute()->toArray();
// Get LDAP Data
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);
$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)) {
foreach($ldap_data as $key => $data) {
$userdata['ldap'][$key] = array_shift($data);
}
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
$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());
foreach ($groups as $group) {
$attributes = $group->getAttributes();
$user_group = array_shift($attributes[$group_indentifier]);
$user_groups[] = $user_group;
if ($this->config->get('plugins.login-ldap.store_ldap_data', false)) {
$userdata['ldap']['groups'][] = $user_group;
}
}
}
// Merge the LDAP user data with Grav user
$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'); $current_access = $grav_user->get('access');
if (!$current_access) { $access = $this->config->get('plugins.login-ldap.default_access_levels.access.site');
$access = $this->config->get('plugins.login.user_registration.access.site', []);
if (!$current_access && $access) {
if (count($access) > 0) { if (count($access) > 0) {
$data['access']['site'] = $access; $data['access']['site'] = $access;
$grav_user->merge($data); $grav_user->merge($data);
} }
} }
$grav_user->merge($userdata); // Give Admin Access
$grav_user->save(); $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)) {
$access_levels = Utils::arrayMergeRecursiveUnique($grav_user->access, $group_access);
$grav_user->merge(['access' => $access_levels]);
}
}
}
// Optional save
if ($this->config->get('plugins.login-ldap.save_grav_user', false)) {
$grav_user->save();
}
$event->setUser($grav_user); $event->setUser($grav_user);
@@ -113,7 +237,14 @@ class LoginLDAPPlugin extends Plugin
return; return;
} catch (ConnectionException $e) { } catch (ConnectionException $e) {
print $e->getMessage(); $message = $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->setStatus($event::AUTHENTICATION_FAILURE);
$event->stopPropagation(); $event->stopPropagation();
@@ -138,4 +269,16 @@ class LoginLDAPPlugin extends Plugin
// This gets fired on user logout. // 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;
}
} }

View File

@@ -5,10 +5,21 @@ version: 3
ssl: false ssl: false
start_tls: false start_tls: false
opt_referrals: false opt_referrals: false
user_dn: uid=[username],dc=company,dc=com user_dn: 'uid=[username],dc=company,dc=com'
search_dn: dc=company,dc=com search_dn:
group_dn:
group_query: '(&(cn=*)(memberUid=[username]))'
group_indentifier: cn
map_username: uid map_username: uid
map_fullname: givenName lastName map_fullname: givenName lastName
map_email: mail map_email: mail
save_grav_user: false
store_ldap_data: false
default_access_levels:
groups:
- ldap_users
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"