Compare commits

...

4 commits

Author SHA1 Message Date
Benjamin Renard
a4e21e6046
Auth\Db: add edit_user CLI command 2024-09-15 09:39:22 +02:00
Benjamin Renard
a74c23fcd0
Auth\Db: implement update_user() 2024-09-15 09:39:21 +02:00
Benjamin Renard
d42a864901
Auth\Db::add_user(): add check of username uniqueness 2024-09-15 09:39:19 +02:00
Benjamin Renard
b33a3e8205
Auth\User: implement array access interface 2024-09-15 09:39:18 +02:00
2 changed files with 191 additions and 8 deletions

View file

@ -8,6 +8,8 @@ use EesyPHP\Log;
use Exception; use Exception;
use function EesyPHP\vardump;
class Db extends Backend { class Db extends Backend {
/** /**
@ -70,8 +72,19 @@ class Db extends Backend {
self :: $password_field = App::get('auth.db.password_field', null, 'string'); self :: $password_field = App::get('auth.db.password_field', null, 'string');
self :: $exposed_fields = App::get('auth.db.exposed_fields', null, 'array'); self :: $exposed_fields = App::get('auth.db.exposed_fields', null, 'array');
if (App :: get('cli.enabled')) if (App :: get('cli.enabled')) {
Cli :: add_command('add_user', ['\\EesyPHP\\Auth\\Db', 'cli_add_user'], 'Add user'); Cli :: add_command(
'add_user',
['\\EesyPHP\\Auth\\Db', 'cli_add_user'],
'Add user'
);
Cli :: add_command(
'edit_user',
['\\EesyPHP\\Auth\\Db', 'cli_edit_user'],
'Edit user',
'[username]'
);
}
return true; return true;
} }
@ -138,18 +151,29 @@ class Db extends Backend {
*/ */
public static function add_user($info) { public static function add_user($info) {
$values = [ $values = [
App::get('auth.db.username_field') => $info['username'], self :: $username_field => $info['username'] ?? null,
App::get('auth.db.password_field') => password_hash( self :: $password_field => (
$info['password'], ($info['password'] ?? null)?
constant('PASSWORD_'.strtoupper(App::get('auth.db.password_hash_algo'))) password_hash(
$info['password'],
constant('PASSWORD_'.strtoupper(App::get('auth.db.password_hash_algo')))
):
null
), ),
]; ];
foreach($info as $field => $value) { foreach($info as $field => $value) {
if (!$value) { if (!$value) {
Log :: error("add_user: field %s is missing", $field); Log :: error("add_user: field %s is missing (or null)", $field);
return false; return false;
} }
} }
// Check username uniqueness
if (self :: get_user($info['username'])) {
Log :: error("add_user: a user with username %s already exist");
return false;
}
foreach(App :: get('auth.db.exposed_fields') as $field) foreach(App :: get('auth.db.exposed_fields') as $field)
if (isset($info[$field]) && $info[$field]) if (isset($info[$field]) && $info[$field])
$values[$field] = $info[$field]; $values[$field] = $info[$field];
@ -162,6 +186,83 @@ class Db extends Backend {
return false; return false;
} }
/**
* Update a user in database
* @param \EesyPHP\Auth\User $user The user object
* @param array<string,mixed> $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("update_user(%s): changes=%s", $user->username, vardump($changes));
if (!$user->username) {
Log::error("update_user(): Invalid user provided (no username)");
return false;
}
if (!is_array($changes)) {
Log::error("update_user(%s): Invalid changes provided (not an array)", $user->username);
return false;
}
$values = [];
foreach($changes as $field => $value) {
switch ($field) {
case "username":
if ($value != $user->username) {
// Check username uniqueness
if (self :: get_user($value)) {
Log :: error(
"update_user(%s): invalid new username '%s': another user with this username already exist",
$user->username,
$value
);
return false;
}
$values[self :: $username_field] = $value;
}
break;
case "password":
if (!password_verify($value, $user["password"]))
$values[self :: $password_field] = password_hash(
$value,
constant('PASSWORD_'.strtoupper(App::get('auth.db.password_hash_algo')))
);
break;
default:
if (in_array($field, App :: get('auth.db.exposed_fields'))) {
if ($value != $user[$field])
$values[$field] = $values;
break;
}
Log :: error("update_user: unknown field %s", $field);
return false;
}
}
if (empty($values)) {
Log::log(
$no_change_as_success?"DEBUG":"ERROR",
"update_user(%s): no change",
$user->username
);
return $no_change_as_success;
}
Log::debug("update_user(%s): changes=%s", $user->username, vardump($values));
if (
self :: $class :: update(
self :: $users_table,
$values,
[App::get('auth.db.username_field') => $user->username]
)
) {
Log :: info('update_user(%s): user updated', $user->username);
return true;
}
Log :: error('update_user(%s): error adding user', $user->username);
return false;
}
/** /**
* CLI command to add user * CLI command to add user
* @param array $command_args Command arguments * @param array $command_args Command arguments
@ -189,4 +290,41 @@ class Db extends Backend {
} }
Log :: fatal("Error occurred adding user %s", $info['username']); Log :: fatal("Error occurred adding user %s", $info['username']);
} }
/**
* CLI command to edit an existing user
* @param array $command_args Command arguments
* @return bool
*/
public static function cli_edit_user($command_args) {
if (count($command_args) != 1)
Cli :: usage();
$username = $command_args[0];
$user = self :: get_user($username);
if (!$user) Cli :: usage("Invalid user '$username'");
$changes = [
"username" => trim(
Cli::ask_user("Please enter user new username [$username]: ")
),
"password" => Cli::ask_user("Please enter user new password [empty = unchange]: ", true),
];
if (!$changes["username"]) unset($changes["username"]);
if (!$changes["password"]) unset($changes["password"]);
foreach(self :: $exposed_fields as $field) {
$value = Cli::ask_user("Please enter user $field [{$user[$field]}]: ");
if (empty($value))
continue;
$changes[$field] = $value;
}
if (!$changes) {
print("No change.\n");
return true;
}
if (self :: update_user($user, $changes)) {
printf("User %s updated\n", $username);
return true;
}
Log :: fatal("Error occurred updating user %s", $username);
}
} }

View file

@ -7,7 +7,7 @@ use EesyPHP\Log;
use function EesyPHP\ensure_is_array; use function EesyPHP\ensure_is_array;
use function EesyPHP\vardump; use function EesyPHP\vardump;
class User { class User implements \ArrayAccess {
/** /**
* Username * Username
@ -108,6 +108,51 @@ class User {
} }
} }
/**
* ArrayAccess interface
*/
#[\ReturnTypeWillChange]
/**
* Magic method to check if an dynamic property exist when object is used as an array
* @param string $offset The property
* @return bool
*/
public function offsetExists($offset) {
return $this -> __isset($offset);
}
#[\ReturnTypeWillChange]
/**
* Magic method to get a dynamic property when object is used as an array
* @param string $offset The property
* @return mixed
*/
public function offsetGet($offset){
return $this -> __get($offset);
}
#[\ReturnTypeWillChange]
/**
* Magic method to set a dynamic property when object is used as an array
* @param string $offset The property
* @param mixed $value The value
* @return void
*/
public function offsetSet($offset, $value) {
$this -> __set($offset, $value);
}
#[\ReturnTypeWillChange]
/**
* Magic method to unset (=set as null) a dynamic property when object is used as an array
* @param string $offset The property
* @return void
*/
public function offsetUnset($offset) {
$this -> __set($offset, null);
}
/** /**
* Check user password * Check user password
* @param string $password * @param string $password