From 29f95e0c4dd4b79ae29b83f7ab7e9b32ee49d889 Mon Sep 17 00:00:00 2001 From: "roberto.gerola" Date: Tue, 27 Dec 2022 09:11:50 +0100 Subject: [PATCH 1/6] Added spark user command to manage users --- src/Commands/User.php | 459 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100644 src/Commands/User.php diff --git a/src/Commands/User.php b/src/Commands/User.php new file mode 100644 index 000000000..e90c31e50 --- /dev/null +++ b/src/Commands/User.php @@ -0,0 +1,459 @@ +'; + + /** + * Command's Arguments + * + * @var array + */ + protected $arguments = [ + 'action' => 'Valid actions : create, activate, deactivate, changename, changeemail, delete, password, list, addgroup, removegroup', + ]; + + /** + * Command's Options + * + * @var array + */ + protected $options = [ + '-i' => 'User id', + '-u' => 'User name', + '-e' => 'User email', + '-nu' => 'New username', + '-ne' => 'New email', + '-g' => 'Group name', + ]; + + /** + * Validation rules for user fields + */ + private array $validation_rules = [ + 'username' => 'required|is_unique[users.username]', + 'email' => 'required|valid_email|is_unique[auth_identities.secret]', + 'password' => 'required|min_length[10]', + ]; + + /** + * Displays the help for the spark cli script itself. + */ + public function run(array $params): void + { + $action = CLI::getSegment(2); + if ($action && in_array($action, $this->valid_actions, true)) { + $userid = (int) CLI::getOption('i'); + $username = CLI::getOption('u'); + $email = CLI::getOption('e'); + $new_username = CLI::getOption('nu'); + $new_email = CLI::getOption('ne'); + $group = CLI::getOption('g'); + + switch ($action) { + case 'create': + $this->create($username, $email); + break; + + case 'activate': + $this->activate($username, $email); + break; + + case 'deactivate': + $this->deactivate($username, $email); + break; + + case 'changename': + $this->changename($username, $email, $new_username); + break; + + case 'changeemail': + $this->changeemail($username, $email, $new_email); + break; + + case 'delete': + $this->delete($userid, $username, $email); + break; + + case 'password': + $this->password($username, $email); + break; + + case 'list': + $this->list($username, $email); + break; + + case 'addgroup': + $this->addgroup($group, $username, $email); + break; + + case 'removegroup': + $this->removegroup($group, $username, $email); + break; + } + } + } + + /** + * Create a new user + * + * @param $username User name to create (optional) + * @param $email User email to create (optional) + */ + private function create(?string $username = null, ?string $email = null): void + { + $data = []; + + if (! $username) { + $username = CLI::prompt('Username', null, $this->validation_rules['username']); + } + $data['username'] = $username; + + if (! $email) { + $email = CLI::prompt('Email', null, $this->validation_rules['email']); + } + $data['email'] = $email; + + $password = CLI::prompt('Password', null, $this->validation_rules['password']); + $password_confirm = CLI::prompt('Password confirmation', null, $this->validation_rules['password']); + + if ($password !== $password_confirm) { + CLI::write("The passwords don't match", 'red'); + + exit; + } + $data['password'] = $password; + + // Run validation if the user has passed username and/or email via command line + $validation = Services::validation(); + $validation->setRules($this->validation_rules); + if (! $validation->run($data)) { + foreach ($validation->getErrors() as $message) { + CLI::write($message, 'red'); + } + CLI::write('User creation aborted', 'red'); + + exit; + } + + $users = model('CodeIgniter\Shield\Models\UserModel'); + $user = new \CodeIgniter\Shield\Entities\User($data); + $users->save($user); + CLI::write('User ' . $username . ' created', 'green'); + } + + /** + * Activate an existing user by username or email + * + * @param $username User name to search for (optional) + * @param $email User email to search for (optional) + */ + private function activate(?string $username = null, ?string $email = null): void + { + $user = $this->findUser('Activate user', $username, $email); + $confirm = CLI::prompt('Activate the user ' . $user->username . ' ?', ['y', 'n']); + if ($confirm === 'y') { + $userModel = model('CodeIgniter\Shield\Models\UserModel'); + $user->active = 1; + $userModel->save($user); + CLI::write('User ' . $user->username . ' activated', 'green'); + } else { + CLI::write('User ' . $user->username . ' activation cancelled', 'yellow'); + } + } + + /** + * Deactivate an existing user by username or email + * + * @param $username User name to search for (optional) + * @param $email User email to search for (optional) + */ + private function deactivate(?string $username = null, ?string $email = null): void + { + $user = $this->findUser('Deactivate user', $username, $email); + $confirm = CLI::prompt('Deactivate the user ' . $username . ' ?', ['y', 'n']); + if ($confirm === 'y') { + $userModel = model('CodeIgniter\Shield\Models\UserModel'); + $user->active = 0; + $userModel->save($user); + CLI::write('User ' . $user->username . ' deactivated', 'green'); + } else { + CLI::write('User ' . $user->username . ' deactivation cancelled', 'yellow'); + } + } + + /** + * Change the name of an existing user by username or email + * + * @param $username User name to search for (optional) + * @param $email User email to search for (optional) + * @param $new_username User new name (optional) + */ + private function changename(?string $username = null, ?string $email = null, ?string $new_username = null): void + { + $validation = Services::validation(); + $validation->setRules([ + 'username' => 'required|is_unique[users.username]', + ]); + + $user = $this->findUser('Change username', $username, $email); + + if (! $new_username) { + $new_username = CLI::prompt('New username', null, $this->validation_rules['username']); + } else { + // Run validation if the user has passed username and/or email via command line + $validation = Services::validation(); + $validation->setRules([ + 'username' => $this->validation_rules['username'], + ]); + if (! $validation->run(['username' => $new_username])) { + foreach ($validation->getErrors() as $message) { + CLI::write($message, 'red'); + } + CLI::write('User name change aborted', 'red'); + + exit; + } + } + + $userModel = model('CodeIgniter\Shield\Models\UserModel'); + $old_username = $user->username; + $user->username = $new_username; + $userModel->save($user); + CLI::write('Username ' . $old_username . ' changed to ' . $new_username, 'green'); + } + + /** + * Change the email of an existing user by username or email + * + * @param $username User name to search for (optional) + * @param $email User email to search for (optional) + * @param $new_email User new email (optional) + */ + private function changeemail(?string $username = null, ?string $email = null, ?string $new_email = null): void + { + $user = $this->findUser('Change email', $username, $email); + + if (! $new_email) { + $new_email = CLI::prompt('New email', null, $this->validation_rules['email']); + } else { + // Run validation if the user has passed username and/or email via command line + $validation = Services::validation(); + $validation->setRules([ + 'email' => $this->validation_rules['email'], + ]); + if (! $validation->run(['email' => $new_email])) { + foreach ($validation->getErrors() as $message) { + CLI::write($message, 'red'); + } + CLI::write('User email change aborted', 'red'); + + exit; + } + } + + $userModel = model('CodeIgniter\Shield\Models\UserModel'); + $user->email = $new_email; + $userModel->save($user); + CLI::write('Email for the user : ' . $user->username . ' changed to ' . $new_email, 'green'); + } + + /** + * Delete an existing user by username or email + * + * @param $userid User id to delete (optional) + * @param $username User name to search for (optional) + * @param $email User email to search for (optional) + */ + private function delete(int $userid = 0, ?string $username = null, ?string $email = null): void + { + if ($userid) { + $user = model('CodeIgniter\Shield\Models\UserModel')->findById($userid); + if (! $user) { + CLI::write("User doesn't exist", 'red'); + + exit; + } + } else { + $user = $this->findUser('Delete user', $username, $email); + } + + $confirm = CLI::prompt('Delete the user ' . $user->username . ' (' . $user->email . ') ?', ['y', 'n']); + if ($confirm === 'y') { + $userModel = model('CodeIgniter\Shield\Models\UserModel'); + $userModel->delete($user->id, true); + CLI::write('User ' . $user->username . ' deleted', 'green'); + } else { + CLI::write('User ' . $user->username . ' deletion cancelled', 'yellow'); + } + } + + /** + * Change the password of an existing user by username or email + * + * @param $username User name to search for (optional) + * @param $email User email to search for (optional) + */ + private function password($username = null, $email = null): void + { + $user = $this->findUser('Change user password', $username, $email); + + $confirm = CLI::prompt('Set the password for the user ' . $user->username . ' ?', ['y', 'n']); + if ($confirm === 'y') { + $password = CLI::prompt('Password', null, 'required'); + $password_confirm = CLI::prompt('Password confirmation', null, $this->validation_rules['password']); + + if ($password !== $password_confirm) { + CLI::write("The passwords don't match", 'red'); + + exit; + } + + $userModel = model('CodeIgniter\Shield\Models\UserModel'); + $user->password = $password; + $userModel->save($user); + + CLI::write('Password for the user ' . $user->username . ' set', 'green'); + } else { + CLI::write('Password setting for the user : ' . $user->username . ', cancelled', 'yellow'); + } + } + + /** + * List users searching by username or email + * + * @param $username User name to search for (optional) + * @param $email User email to search for (optional) + */ + private function list($username = null, $email = null): void + { + $users = model('CodeIgniter\Shield\Models\UserModel'); + $users = $users->join('auth_identities', 'auth_identities.user_id = users.id'); + if ($username) { + $users = $users->like('username', $username); + } + if ($email) { + $users = $users->like('secret', $email); + } + + CLI::write("Id\tUser"); + + foreach ($users->findAll() as $user) { + CLI::write($user->id . "\t" . $user->username . ' (' . $user->secret . ')'); + } + } + + /** + * Add a user by username or email to a group + * + * @param $group Group to add user to + * @param $username User name to search for (optional) + * @param $email User email to search for (optional) + */ + private function addgroup(string $group, $username = null, $email = null): void + { + $user = $this->findUser('Add user to group', $username, $email); + + $confirm = CLI::prompt('Add the user: ' . $user->username . ' to the group: ' . $group . ' ?', ['y', 'n']); + if ($confirm === 'y') { + $user->addGroup($group); + CLI::write('User ' . $user->username . ' added to group ' . $group, 'green'); + } else { + CLI::write('Addition of the user: ' . $user->username . ' to the group: ' . $group . ' cancelled', 'yellow'); + } + } + + /** + * Remove a user by username or email from a group + * + * @param $group Group to remove user from + * @param $username User name to search for (optional) + * @param $email User email to search for (optional) + */ + private function removegroup(string $group, $username = null, $email = null): void + { + $user = $this->findUser('Remove user from group', $username, $email); + + $confirm = CLI::prompt('Remove the user: ' . $user->username . ' fromt the group: ' . $group . ' ?', ['y', 'n']); + if ($confirm === 'y') { + $user->removeGroup($group); + CLI::write('User ' . $user->username . ' removed from group ' . $group, 'green'); + } else { + CLI::write('Removal of the user: ' . $user->username . ' from the group: ' . $group . ' cancelled', 'yellow'); + } + } + + /** + * Find an existing user by username or email. Exit if a user is not found + * + * @param $question Initial question at user prompt + * @param $username User name to search for (optional) + * @param $email User email to search for (optional) + */ + private function findUser($question = '', $username = null, $email = null): \CodeIgniter\Shield\Entities\User + { + if (! $username && ! $email) { + $choice = CLI::prompt($question . ' by username or email ?', ['u', 'e']); + if ($choice === 'u') { + $username = CLI::prompt('Username', null, 'required'); + } elseif ($choice === 'e') { + $email = CLI::prompt('Email', null, 'required|valid_email'); + } + } + + $user = new \CodeIgniter\Shield\Entities\User(); + $users = model('CodeIgniter\Shield\Models\UserModel'); + $users = $users->join('auth_identities', 'auth_identities.user_id = users.id'); + + if ($username) { + $user = $users->where('username', $username)->first(); + } elseif ($email) { + $user = $users->where('secret', $email)->first(); + } + + if (! $user) { + CLI::write("User doesn't exist", 'red'); + + exit; + } + + return $user; + } +} From 99c53a305459a8a5de6d6055d163fcb5d906daf2 Mon Sep 17 00:00:00 2001 From: "roberto.gerola" Date: Tue, 27 Dec 2022 17:30:07 +0100 Subject: [PATCH 2/6] Added warning on call without action and group option for group related actions --- src/Commands/User.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Commands/User.php b/src/Commands/User.php index e90c31e50..d97129d75 100644 --- a/src/Commands/User.php +++ b/src/Commands/User.php @@ -121,14 +121,27 @@ public function run(array $params): void break; case 'addgroup': - $this->addgroup($group, $username, $email); + if(!$group) { + CLI::write('Group option is missing', 'red'); + } + else { + $this->addgroup($group, $username, $email); + } break; case 'removegroup': - $this->removegroup($group, $username, $email); + if(!$group) { + CLI::write('Group option is missing', 'red'); + } + else { + $this->removegroup($group, $username, $email); + } break; } } + else { + CLI::write('Specify a valid action : ' . implode(',', $this->valid_actions), 'red'); + } } /** From 8297e36b6c1850374d664792a1a195a5e176ce32 Mon Sep 17 00:00:00 2001 From: "roberto.gerola" Date: Mon, 2 Jan 2023 09:57:59 +0100 Subject: [PATCH 3/6] Ask for group if not specified in group related actions --- src/Commands/User.php | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Commands/User.php b/src/Commands/User.php index d97129d75..855173ddd 100644 --- a/src/Commands/User.php +++ b/src/Commands/User.php @@ -121,25 +121,14 @@ public function run(array $params): void break; case 'addgroup': - if(!$group) { - CLI::write('Group option is missing', 'red'); - } - else { - $this->addgroup($group, $username, $email); - } + $this->addgroup($group, $username, $email); break; case 'removegroup': - if(!$group) { - CLI::write('Group option is missing', 'red'); - } - else { - $this->removegroup($group, $username, $email); - } + $this->removegroup($group, $username, $email); break; } - } - else { + } else { CLI::write('Specify a valid action : ' . implode(',', $this->valid_actions), 'red'); } } @@ -400,8 +389,12 @@ private function list($username = null, $email = null): void * @param $username User name to search for (optional) * @param $email User email to search for (optional) */ - private function addgroup(string $group, $username = null, $email = null): void + private function addgroup($group = null, $username = null, $email = null): void { + if (! $group) { + $group = CLI::prompt('Group', null, 'required'); + } + $user = $this->findUser('Add user to group', $username, $email); $confirm = CLI::prompt('Add the user: ' . $user->username . ' to the group: ' . $group . ' ?', ['y', 'n']); @@ -420,8 +413,12 @@ private function addgroup(string $group, $username = null, $email = null): void * @param $username User name to search for (optional) * @param $email User email to search for (optional) */ - private function removegroup(string $group, $username = null, $email = null): void + private function removegroup($group = null, $username = null, $email = null): void { + if (! $group) { + $group = CLI::prompt('Group', null, 'required'); + } + $user = $this->findUser('Remove user from group', $username, $email); $confirm = CLI::prompt('Remove the user: ' . $user->username . ' fromt the group: ' . $group . ' ?', ['y', 'n']); From 5fe9a18b2fe85855a06174d5ed6ea91fede7be19 Mon Sep 17 00:00:00 2001 From: "roberto.gerola" Date: Thu, 5 Jan 2023 10:17:40 +0100 Subject: [PATCH 4/6] Changed the options for passing new username (-n) and new email (-m) --- src/Commands/User.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Commands/User.php b/src/Commands/User.php index 855173ddd..72926cf37 100644 --- a/src/Commands/User.php +++ b/src/Commands/User.php @@ -56,12 +56,12 @@ class User extends BaseCommand * @var array */ protected $options = [ - '-i' => 'User id', - '-u' => 'User name', - '-e' => 'User email', - '-nu' => 'New username', - '-ne' => 'New email', - '-g' => 'Group name', + '-i' => 'User id', + '-u' => 'User name', + '-e' => 'User email', + '-n' => 'New username', + '-m' => 'New email', + '-g' => 'Group name', ]; /** @@ -83,8 +83,8 @@ public function run(array $params): void $userid = (int) CLI::getOption('i'); $username = CLI::getOption('u'); $email = CLI::getOption('e'); - $new_username = CLI::getOption('nu'); - $new_email = CLI::getOption('ne'); + $new_username = CLI::getOption('n'); + $new_email = CLI::getOption('m'); $group = CLI::getOption('g'); switch ($action) { @@ -364,7 +364,7 @@ private function password($username = null, $email = null): void * @param $username User name to search for (optional) * @param $email User email to search for (optional) */ - private function list($username = null, $email = null): void + private function list(?string $username = null, ?string $email = null): void { $users = model('CodeIgniter\Shield\Models\UserModel'); $users = $users->join('auth_identities', 'auth_identities.user_id = users.id'); From 12f85a01323c1b13074d37cc94700ae0e84aaf48 Mon Sep 17 00:00:00 2001 From: "roberto.gerola" Date: Fri, 31 Mar 2023 14:50:31 +0200 Subject: [PATCH 5/6] Improved readability of model loading --- src/Commands/User.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Commands/User.php b/src/Commands/User.php index 72926cf37..dd3816a8b 100644 --- a/src/Commands/User.php +++ b/src/Commands/User.php @@ -175,9 +175,9 @@ private function create(?string $username = null, ?string $email = null): void exit; } - $users = model('CodeIgniter\Shield\Models\UserModel'); + $userModel = model('CodeIgniter\Shield\Models\UserModel'); $user = new \CodeIgniter\Shield\Entities\User($data); - $users->save($user); + $userModel->save($user); CLI::write('User ' . $username . ' created', 'green'); } @@ -306,8 +306,9 @@ private function changeemail(?string $username = null, ?string $email = null, ?s */ private function delete(int $userid = 0, ?string $username = null, ?string $email = null): void { + $userModel = model('CodeIgniter\Shield\Models\UserModel'); if ($userid) { - $user = model('CodeIgniter\Shield\Models\UserModel')->findById($userid); + $user = $userModel->findById($userid); if (! $user) { CLI::write("User doesn't exist", 'red'); @@ -319,7 +320,6 @@ private function delete(int $userid = 0, ?string $username = null, ?string $emai $confirm = CLI::prompt('Delete the user ' . $user->username . ' (' . $user->email . ') ?', ['y', 'n']); if ($confirm === 'y') { - $userModel = model('CodeIgniter\Shield\Models\UserModel'); $userModel->delete($user->id, true); CLI::write('User ' . $user->username . ' deleted', 'green'); } else { @@ -366,8 +366,8 @@ private function password($username = null, $email = null): void */ private function list(?string $username = null, ?string $email = null): void { - $users = model('CodeIgniter\Shield\Models\UserModel'); - $users = $users->join('auth_identities', 'auth_identities.user_id = users.id'); + $userModel = model('CodeIgniter\Shield\Models\UserModel'); + $users = $userModel->join('auth_identities', 'auth_identities.user_id = users.id'); if ($username) { $users = $users->like('username', $username); } @@ -449,8 +449,8 @@ private function findUser($question = '', $username = null, $email = null): \Cod } $user = new \CodeIgniter\Shield\Entities\User(); - $users = model('CodeIgniter\Shield\Models\UserModel'); - $users = $users->join('auth_identities', 'auth_identities.user_id = users.id'); + $userModel = model('CodeIgniter\Shield\Models\UserModel'); + $users = $userModel->join('auth_identities', 'auth_identities.user_id = users.id'); if ($username) { $user = $users->where('username', $username)->first(); From 2cde55039a298eb8848b738a2319d803d007d584 Mon Sep 17 00:00:00 2001 From: "roberto.gerola" Date: Fri, 31 Mar 2023 14:51:23 +0200 Subject: [PATCH 6/6] Improved readability of model loading --- src/Commands/User.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Commands/User.php b/src/Commands/User.php index dd3816a8b..112382f14 100644 --- a/src/Commands/User.php +++ b/src/Commands/User.php @@ -176,7 +176,7 @@ private function create(?string $username = null, ?string $email = null): void } $userModel = model('CodeIgniter\Shield\Models\UserModel'); - $user = new \CodeIgniter\Shield\Entities\User($data); + $user = new \CodeIgniter\Shield\Entities\User($data); $userModel->save($user); CLI::write('User ' . $username . ' created', 'green'); } @@ -367,7 +367,7 @@ private function password($username = null, $email = null): void private function list(?string $username = null, ?string $email = null): void { $userModel = model('CodeIgniter\Shield\Models\UserModel'); - $users = $userModel->join('auth_identities', 'auth_identities.user_id = users.id'); + $users = $userModel->join('auth_identities', 'auth_identities.user_id = users.id'); if ($username) { $users = $users->like('username', $username); } @@ -448,9 +448,9 @@ private function findUser($question = '', $username = null, $email = null): \Cod } } - $user = new \CodeIgniter\Shield\Entities\User(); + $user = new \CodeIgniter\Shield\Entities\User(); $userModel = model('CodeIgniter\Shield\Models\UserModel'); - $users = $userModel->join('auth_identities', 'auth_identities.user_id = users.id'); + $users = $userModel->join('auth_identities', 'auth_identities.user_id = users.id'); if ($username) { $user = $users->where('username', $username)->first();