From 31fcdf377a8337d780dae08805e2f037d0ce1cbe Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Tue, 20 Oct 2020 21:43:58 +0200 Subject: [PATCH] Implemented LDAP authentication support (closes #305) --- changelog/60_UNRELEASED_2020-xx-xx.md | 13 +++-- config-dist.php | 5 ++ middleware/LdapAuthMiddleware.php | 74 +++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 middleware/LdapAuthMiddleware.php diff --git a/changelog/60_UNRELEASED_2020-xx-xx.md b/changelog/60_UNRELEASED_2020-xx-xx.md index 61bb9788..0cff7e05 100644 --- a/changelog/60_UNRELEASED_2020-xx-xx.md +++ b/changelog/60_UNRELEASED_2020-xx-xx.md @@ -14,10 +14,15 @@ - Other users only see their API keys on that page - (Thanks @fipwmaqzufheoxq92ebc) -### New feature: Reverse proxy authenticaton support -- New `config.php` settings `AUTH_CLASS` and `REVERSE_PROXY_AUTH_HEADER` -- If you set `AUTH_CLASS` to `Grocy\Middleware\ReverseProxyAuthMiddleware` and your reverse proxy sends a username in the HTTP header `REMOTE_USER` (header name can be changed by the setting `REVERSE_PROXY_AUTH_HEADER`), the user is automatically authenticated (and will also be created, if not already present) -- (Thanks @fipwmaqzufheoxq92ebc for the initial work on this) +### New feature: External authentication support +- New `config.php` setting `AUTH_CLASS` to change the used authentication provider +- Via LDAP + - New `config.php` settings `LDAP_DOMAIN`, `LDAP_ADDRESS` and `LDAP_BASE_DN` + - If you set `AUTH_CLASS` to `Grocy\Middleware\LdapAuthMiddleware`, users will be authenticated against your directory (and will also be created (in grocy), if not already present) +- Via a reverse proxy + - New `config.php` setting `REVERSE_PROXY_AUTH_HEADER` + - If you set `AUTH_CLASS` to `Grocy\Middleware\ReverseProxyAuthMiddleware` and your reverse proxy sends a username in the HTTP header `REMOTE_USER` (header name can be changed by the setting `REVERSE_PROXY_AUTH_HEADER`), the user is automatically authenticated (and will also be created (in grocy), if not already present) + - (Thanks @fipwmaqzufheoxq92ebc for the initial work on this) ### Stock improvements/fixes - When creating a quantity unit conversion it's now possible to automatically create the inverse conversion (thanks @kriddles) diff --git a/config-dist.php b/config-dist.php index fb8e179b..fa70aaef 100644 --- a/config-dist.php +++ b/config-dist.php @@ -73,6 +73,11 @@ Setting('AUTH_CLASS', 'Grocy\Middleware\DefaultAuthMiddleware'); // the name of the HTTP header which your reverse proxy uses to pass the username (on successful authentication) Setting('REVERSE_PROXY_AUTH_HEADER', 'REMOTE_USER'); +// When using LdapAuthMiddleware +Setting('LDAP_DOMAIN', ''); // Example value "local" +Setting('LDAP_ADDRESS', ''); // Example value "ldap://vm-dc2019.local.berrnd.net" +Setting('LDAP_BASE_DN', ''); // Example value "OU=OU_Users,DC=local,DC=berrnd,DC=net" + // Set this to true if you want to disable the ability to scan a barcode via the device camera (Browser API) Setting('DISABLE_BROWSER_BARCODE_CAMERA_SCANNING', false); diff --git a/middleware/LdapAuthMiddleware.php b/middleware/LdapAuthMiddleware.php new file mode 100644 index 00000000..f1495d5c --- /dev/null +++ b/middleware/LdapAuthMiddleware.php @@ -0,0 +1,74 @@ +authenticate somehow + + // First try to authenticate by API key + $auth = new ApiKeyAuthMiddleware($this->AppContainer, $this->ResponseFactory); + $user = $auth->authenticate($request); + + if ($user !== null) + { + return $user; + } + + // Then by session cookie + $auth = new SessionAuthMiddleware($this->AppContainer, $this->ResponseFactory); + $user = $auth->authenticate($request); + return $user; + } + + public static function ProcessLogin(array $postParams) + { + if ($connect = ldap_connect(GROCY_LDAP_ADDRESS)) + { + ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3); + ldap_set_option($connect, LDAP_OPT_REFERRALS, 0); + + if ($bind = ldap_bind($connect, GROCY_LDAP_DOMAIN . '\\' . $postParams['username'], $postParams['password'])) + { + $fields = '(|(samaccountname=*' . $postParams['username'] . '*))'; + + $search = ldap_search($connect, GROCY_LDAP_BASE_DN, $fields); + $result = ldap_get_entries($connect, $search); + + $ldapFirstName = $result[0]['givenname'][0]; + $ldapLastName = $result[0]['sn'][0]; + + ldap_close($connect); + + $db = DatabaseService::getInstance()->GetDbConnection(); + $user = $db->users()->where('username', $postParams['username'])->fetch(); + if ($user == null) + { + $user = UsersService::getInstance()->CreateUser($postParams['username'], $ldapFirstName, $ldapLastName, ''); + } + + $sessionKey = SessionService::getInstance()->CreateSession($user->id, $postParams['stay_logged_in'] == 'on'); + self::SetSessionCookie($sessionKey); + + return true; + } + else + { + // LDAP authentication failed + return false; + } + } + else + { + // LDAP connection failed + return false; + } + } +}