From 89e29b8add17fe6c26daae64b05db2594b775f2e Mon Sep 17 00:00:00 2001 From: Benjamin Renard Date: Tue, 4 Jun 2024 11:36:16 +0200 Subject: [PATCH] Auth: Add update_user() method on backends and save() method on user --- src/Auth/Backend.php | 12 +++++++++ src/Auth/Ldap.php | 63 ++++++++++++++++++++++++++++++++++++++++++++ src/Auth/User.php | 39 +++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/src/Auth/Backend.php b/src/Auth/Backend.php index 10ff60e..5ab562f 100644 --- a/src/Auth/Backend.php +++ b/src/Auth/Backend.php @@ -21,6 +21,18 @@ class Backend { return null; } + /** + * Update a user + * @param \EesyPHP\Auth\User $user The user object + * @param array $changes Array of changes + * @param boolean $no_change_as_success Consider no change provided as success + * (optional, default: false) + * @return boolean True if user was updated, false otherwise + */ + public static function update_user($user, $changes, $no_change_as_success=False) { + return false; + } + /** * Check a user password * @param \EesyPHP\Auth\User $user The user object diff --git a/src/Auth/Ldap.php b/src/Auth/Ldap.php index 1b2f596..f8f6633 100644 --- a/src/Auth/Ldap.php +++ b/src/Auth/Ldap.php @@ -263,6 +263,69 @@ class Ldap extends Backend { return new User($username, '\\EesyPHP\\Auth\\Ldap', $info); } + /** + * Update a user + * @param \EesyPHP\Auth\User $user The user object + * @param array $changes Array of changes + * @param boolean $no_change_as_success Consider no change provided as success + * (optional, default: false) + * @return boolean True if user was updated, false otherwise + */ + public static function update_user($user, $changes, $no_change_as_success=False) { + Log::debug("Ldap::update_user(%s): changes=%s", $user->dn, vardump($changes)); + if (!$user->dn) { + Log::error("Ldap::update_user(): Invalid user provided (no DN)"); + return false; + } + if (!is_array($changes)) { + Log::error("Ldap::update_user(%s): Invalid changes provided (not an array)", $user->dn); + return false; + } + $attrs = App::get('auth.ldap.user_attributes', null, 'array'); + $updated_attrs = []; + $deleted_attrs = []; + foreach($changes as $attr => $value) { + if ($attr == "dn") continue; + if (!array_key_exists($attr, $attrs)) { + Log::error( + "Ldap::update_user(%s): Changes on unknown attribute %s provided", + $user->dn, $attr + ); + return false; + } + $ldap_name = Config::get("$attr.ldap_name", $attr, 'string', false, $attrs); + if ($value) + $updated_attrs[$ldap_name] = ensure_is_array($value); + else + $deleted_attrs[] = $ldap_name; + } + if (empty($updated_attrs) && empty($deleted_attrs)) { + Log::debug("Ldap::update_user(%s): no change provided", $user->dn); + return $no_change_as_success; + } + if (!self :: connect()) return false; + // @phpstan-ignore-next-line + $entry = self :: $connection -> getEntry($user->dn); + // @phpstan-ignore-next-line + if (Net_LDAP2::isError($entry)) { + Log::warning('User "%s" (%s) not found', $user->username, $user->dn); + return false; + } + if ($updated_attrs) $entry->replace($updated_attrs); + if ($deleted_attrs) $entry->delete($deleted_attrs); + $result = $entry -> update(); + // @phpstan-ignore-next-line + if (Net_LDAP2::isError($result)) { + Log::error( + 'Fail to update user "%s" (%s): %s', + $user->username, $user->dn, $result->getMessage() + ); + return false; + } + Log::info('User "%s" (%s) updated', $user->username, $user->dn); + return true; + } + /** * Check a user password * @param \EesyPHP\Auth\User $user The user object diff --git a/src/Auth/User.php b/src/Auth/User.php index 922cf4c..e39bd6c 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -2,8 +2,10 @@ namespace EesyPHP\Auth; +use EesyPHP\Hook; use EesyPHP\Log; use function EesyPHP\ensure_is_array; +use function EesyPHP\vardump; class User { @@ -25,6 +27,12 @@ class User { */ private $info; + /** + * Original user object (set on change and keep to handle update) + * @var User|null + */ + private $original_user; + /** * Constructor * @param string $username The username @@ -66,6 +74,7 @@ class User { * @return void */ public function __set($key, $value) { + Log::trace("%s->__set(%s): new value=%s", $this, $key, vardump($value)); switch ($key) { case 'username': $this -> username = strval($value); @@ -77,6 +86,8 @@ class User { $this -> info = ensure_is_array($value); break; default: + if (!isset($this ->original_user)) + $this -> original_user = new User($this->username, $this->backend, $this->info); $this -> info[$key] = $value; } } @@ -119,4 +130,32 @@ class User { return $this -> namename; } + /** + * Update user + * @return bool + */ + public function save() { + if (!isset($this -> original_user)) { + Log::debug("%s->save(): original_user not set, no change to save.", $this); + return true; + } + $changes = []; + foreach ($this->info as $attr => $value) + if ($this->original_user->$attr != $value) + $changes[$attr] = $value; + if (!$changes) { + Log::debug("%s->save(): no info updated, no change to save", $this); + return true; + } + $result = call_user_func( + array($this -> backend, 'update_user'), + $this -> original_user, $changes, true + ); + if ($result) { + unset($this -> original_user); + Hook::trigger("user_updated", ["user" => $this, "changes" => $changes]); + } + return $result; + } + }