From 902be03f2bf556d54eea87a1b0ffa2ebf89a5896 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Tue, 24 Aug 2021 17:35:09 +0200 Subject: [PATCH] new: [CLI] User shell --- app/Console/Command/AdminShell.php | 6 + app/Console/Command/AppShell.php | 18 ++- app/Console/Command/AuthkeyShell.php | 3 +- app/Console/Command/PasswordShell.php | 3 +- app/Console/Command/UserShell.php | 206 ++++++++++++++++++++++++++ app/Model/User.php | 1 + 6 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 app/Console/Command/UserShell.php diff --git a/app/Console/Command/AdminShell.php b/app/Console/Command/AdminShell.php index 2eb8c618d..6a7c65108 100644 --- a/app/Console/Command/AdminShell.php +++ b/app/Console/Command/AdminShell.php @@ -464,6 +464,9 @@ class AdminShell extends AppShell return PHP_EOL . '---------------------------------------------------------------' . PHP_EOL; } + /** + * @deprecated Use UserShell instead + */ public function change_authkey() { $this->ConfigLoad->execute(); @@ -635,6 +638,9 @@ class AdminShell extends AppShell } } + /** + * @deprecated Use UserShell instead + */ public function UserIP() { $this->ConfigLoad->execute(); diff --git a/app/Console/Command/AppShell.php b/app/Console/Command/AppShell.php index b9162f483..33d0684b5 100644 --- a/app/Console/Command/AppShell.php +++ b/app/Console/Command/AppShell.php @@ -30,8 +30,24 @@ class AppShell extends Shell { public $tasks = array('ConfigLoad'); - public function perform() { + public function perform() + { $this->initialize(); $this->{array_shift($this->args)}(); } + + protected function _welcome() + { + // disable welcome message + } + + /** + * @param mixed $data + * @return string + * @throws JsonException + */ + protected function json($data) + { + return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR); + } } diff --git a/app/Console/Command/AuthkeyShell.php b/app/Console/Command/AuthkeyShell.php index 0d74a20e6..067bbb53d 100644 --- a/app/Console/Command/AuthkeyShell.php +++ b/app/Console/Command/AuthkeyShell.php @@ -1,9 +1,10 @@ addSubcommand('list', [ + 'help' => __('Get list of user accounts.'), + 'parser' => [ + 'options' => [ + 'json' => ['help' => __('Output as JSON.'), 'boolean' => true], + ], + ] + ]); + $parser->addSubcommand('block', [ + 'help' => __('Immediately block user.'), + 'parser' => [ + 'arguments' => [ + 'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true], + ], + ], + ]); + $parser->addSubcommand('unblock', [ + 'help' => __('Unblock blocked user.'), + 'parser' => [ + 'arguments' => [ + 'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true], + ], + ], + ]); + $parser->addSubcommand('change_pw', [ + 'help' => __('Change user password.'), + 'parser' => [ + 'arguments' => [ + 'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true], + 'password' => ['help' => __('New user password.'), 'required' => true], + ], + 'options' => [ + 'no_password_change' => ['help' => __('Do not require password change.'), 'boolean' => true], + ], + ], + ]); + $parser->addSubcommand('change_authkey', [ + 'help' => __('Change authkey. When advanced authkeys are enabled, old authkeys will be disabled.'), + 'parser' => [ + 'arguments' => [ + 'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true], + ], + ], + ]); + $parser->addSubcommand('user_ips', [ + 'help' => __('Show IP addresses that user uses to access MISP.'), + 'parser' => [ + 'arguments' => [ + 'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true], + ], + 'options' => [ + 'json' => ['help' => __('Output as JSON.'), 'boolean' => true], + ], + ], + ]); + return $parser; + } + + public function list() + { + // do not fetch sensitive or big values + $schema = $this->User->schema(); + unset($schema['authkey']); + unset($schema['password']); + unset($schema['gpgkey']); + unset($schema['certif_public']); + + $fields = array_keys($schema); + $fields[] = 'Role.*'; + $fields[] = 'Organisation.*'; + + $users = $this->User->find('all', [ + 'recursive' => -1, + 'fields' => $fields, + 'contain' => ['Organisation', 'Role'], + ]); + + if ($this->params['json']) { + $this->out($this->json($users)); + } else { + foreach ($users as $user) { + $this->out($user['User']['email']); + } + } + } + + public function block() + { + list($userId) = $this->args; + $user = $this->getUser($userId); + if ($user['disabled']) { + $this->error("User $userId is already blocked."); + } + $this->User->updateField($user, 'disabled', true); + $this->out("User $userId blocked."); + } + + public function unblock() + { + list($userId) = $this->args; + $user = $this->getUser($userId); + if (!$user['disabled']) { + $this->error("User $userId is not blocked."); + } + $this->User->updateField($user, 'disabled', false); + $this->out("User $userId unblocked."); + } + + public function change_pw() + { + list($userId, $newPassword) = $this->args; + $user = $this->getUser($userId); + + $user['password'] = $newPassword; + $user['confirm_password'] = $newPassword; + $user['change_pw'] = !$this->params['no_password_change']; + + if (!$this->User->save($user)) { + $this->out("Could not update password for user $userId."); + $this->out($this->json($this->User->validationErrors)); + $this->_stop(self::CODE_ERROR); + } + + $this->out("Password for $userId changed."); + } + + public function change_authkey() + { + list($userId) = $this->args; + $user = $this->getUser($userId); + + if (empty(Configure::read('Security.advanced_authkeys'))) { + $oldKey = $user['authkey']; + $newkey = $this->User->generateAuthKey(); + $this->User->updateField($user, 'authkey', $newkey); + $this->Log->createLogEntry('SYSTEM', 'reset_auth_key', 'User', $user['id'], + __('Authentication key for user %s (%s) updated.', $user['id'], $user['email']), + ['authkey' => [$oldKey, $newkey]] + ); + $this->out("Authentication key changed to: $newkey"); + } else { + $newkey = $this->User->AuthKey->resetAuthKey($user['id']); + if ($newkey) { + $this->out("Old authentication keys disabled and new key created: $newkey"); + } else { + $this->error('There is problem with changing auth key.'); + } + } + } + + public function user_ips() + { + list($userId) = $this->args; + $user = $this->getUser($userId); + + if (empty(Configure::read('MISP.log_user_ips'))) { + $this->out('Storing user IP addresses is disabled.'); + } + + $ips = $this->User->setupRedisWithException()->smembers('misp:user_ip:' . $user['id']); + + if ($this->params['json']) { + $this->out($this->json($ips)); + } else { + $this->hr(); + $this->out("User #{$user['id']}: {$user['email']}"); + $this->hr(); + $this->out(implode(PHP_EOL, $ips)); + } + } + + /** + * @param string|int $userId + * @return array + */ + private function getUser($userId) + { + // Do not fetch password from database + $schema = $this->User->schema(); + unset($schema['password']); + + $conditions = is_numeric($userId) ? ['User.id' => $userId] : ['User.email' => $userId]; + $user = $this->User->find('first', [ + 'conditions' => $conditions, + 'recursive' => -1, + 'fields' => array_keys($schema), + ]); + if (empty($user)) { + $this->error("User `$userId` not found."); + } + return $user['User']; + } +} diff --git a/app/Model/User.php b/app/Model/User.php index 9a0615efa..4388cd1d4 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -12,6 +12,7 @@ App::uses('BlowfishPasswordHasherConstant', 'Tools'); * @property Role $Role * @property UserSetting $UserSetting * @property Event $Event + * @property AuthKey $AuthKey */ class User extends AppModel {