From 7bd273f7502c04ec517da531debab04fb204e0a9 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 10 Mar 2022 15:25:22 +0100 Subject: [PATCH 1/4] Trigger an event to log an activity when an administrator generates an app password Signed-off-by: Joas Schilling --- core/Command/User/AddAppPassword.php | 17 +++++++++++------ core/register_command.php | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/core/Command/User/AddAppPassword.php b/core/Command/User/AddAppPassword.php index a29692df0457f..34c8dc67ccc33 100644 --- a/core/Command/User/AddAppPassword.php +++ b/core/Command/User/AddAppPassword.php @@ -23,10 +23,11 @@ */ namespace OC\Core\Command\User; +use OC\Authentication\Events\AppPasswordCreatedEvent; use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IUserManager; -use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\QuestionHelper; @@ -44,17 +45,17 @@ class AddAppPassword extends Command { protected $tokenProvider; /** @var ISecureRandom */ private $random; - /** @var ICrypto */ - private $crypto; + /** @var IEventDispatcher */ + private $eventDispatcher; public function __construct(IUserManager $userManager, IProvider $tokenProvider, ISecureRandom $random, - ICrypto $crypto) { + IEventDispatcher $eventDispatcher) { $this->tokenProvider = $tokenProvider; $this->userManager = $userManager; $this->random = $random; - $this->crypto = $crypto; + $this->eventDispatcher = $eventDispatcher; parent::__construct(); } @@ -112,7 +113,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $token = $this->random->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS); - $this->tokenProvider->generateToken( + $generatedToken = $this->tokenProvider->generateToken( $token, $user->getUID(), $user->getUID(), @@ -122,6 +123,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int IToken::DO_NOT_REMEMBER ); + $this->eventDispatcher->dispatchTyped( + new AppPasswordCreatedEvent($generatedToken) + ); + $output->writeln('app password:'); $output->writeln($token); diff --git a/core/register_command.php b/core/register_command.php index 3cff363e46fe3..7385de64b5d70 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -187,7 +187,7 @@ $application->add(new OC\Core\Command\User\Setting(\OC::$server->getUserManager(), \OC::$server->getConfig(), \OC::$server->getDatabaseConnection())); $application->add(new OC\Core\Command\User\ListCommand(\OC::$server->getUserManager(), \OC::$server->getGroupManager())); $application->add(new OC\Core\Command\User\Info(\OC::$server->getUserManager(), \OC::$server->getGroupManager())); - $application->add(new OC\Core\Command\User\AddAppPassword(\OC::$server->get(\OCP\IUserManager::class), \OC::$server->get(\OC\Authentication\Token\IProvider::class), \OC::$server->get(\OCP\Security\ISecureRandom::class), \OC::$server->get(\OCP\Security\ICrypto::class))); + $application->add(new OC\Core\Command\User\AddAppPassword(\OC::$server->get(\OCP\IUserManager::class), \OC::$server->get(\OC\Authentication\Token\IProvider::class), \OC::$server->get(\OCP\Security\ISecureRandom::class), \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class))); $application->add(new OC\Core\Command\Group\Add(\OC::$server->getGroupManager())); $application->add(new OC\Core\Command\Group\Delete(\OC::$server->getGroupManager())); From a96cb00505dc927a3d789c3fc283737eeb4e3014 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 10 Mar 2022 15:26:47 +0100 Subject: [PATCH 2/4] Differenciate the activity depending on admin vs user action Signed-off-by: Joas Schilling --- apps/settings/lib/Activity/Provider.php | 6 +++++- .../lib/Listener/AppPasswordCreatedActivityListener.php | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/settings/lib/Activity/Provider.php b/apps/settings/lib/Activity/Provider.php index 2d5c858f5e83b..a6314fdfb11e9 100644 --- a/apps/settings/lib/Activity/Provider.php +++ b/apps/settings/lib/Activity/Provider.php @@ -115,7 +115,11 @@ public function parse($language, IEvent $event, IEvent $previousEvent = null): I } elseif ($event->getSubject() === self::EMAIL_CHANGED) { $subject = $this->l->t('Your email address was changed by an administrator'); } elseif ($event->getSubject() === self::APP_TOKEN_CREATED) { - $subject = $this->l->t('You created app password "{token}"'); + if ($event->getAffectedUser() === $event->getAuthor()) { + $subject = $this->l->t('You created app password "{token}"'); + } else { + $subject = $this->l->t('An administrator created app password "{token}"'); + } } elseif ($event->getSubject() === self::APP_TOKEN_DELETED) { $subject = $this->l->t('You deleted app password "{token}"'); } elseif ($event->getSubject() === self::APP_TOKEN_RENAMED) { diff --git a/apps/settings/lib/Listener/AppPasswordCreatedActivityListener.php b/apps/settings/lib/Listener/AppPasswordCreatedActivityListener.php index 3eec74f460404..587d626ef9778 100644 --- a/apps/settings/lib/Listener/AppPasswordCreatedActivityListener.php +++ b/apps/settings/lib/Listener/AppPasswordCreatedActivityListener.php @@ -31,6 +31,7 @@ use OCP\Activity\IManager as IActivityManager; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; +use OCP\IUserSession; use Psr\Log\LoggerInterface; /** @@ -40,12 +41,17 @@ class AppPasswordCreatedActivityListener implements IEventListener { /** @var IActivityManager */ private $activityManager; + /** @var IUserSession */ + private $userSession; + /** @var LoggerInterface */ private $logger; public function __construct(IActivityManager $activityManager, + IUserSession $userSession, LoggerInterface $logger) { $this->activityManager = $activityManager; + $this->userSession = $userSession; $this->logger = $logger; } @@ -58,7 +64,7 @@ public function handle(Event $event): void { $activity->setApp('settings') ->setType('security') ->setAffectedUser($event->getToken()->getUID()) - ->setAuthor($event->getToken()->getUID()) + ->setAuthor($this->userSession->getUser() ? $this->userSession->getUser()->getUID() : '') ->setSubject(Provider::APP_TOKEN_CREATED, ['name' => $event->getToken()->getName()]) ->setObject('app_token', $event->getToken()->getId()); From 579f07ee2a525eab161da10280ab0cc26f85df64 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 10 Mar 2022 15:28:01 +0100 Subject: [PATCH 3/4] Validate the password before generating an apptoken Signed-off-by: Joas Schilling --- core/Command/User/AddAppPassword.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/Command/User/AddAppPassword.php b/core/Command/User/AddAppPassword.php index 34c8dc67ccc33..4f636c406fbaa 100644 --- a/core/Command/User/AddAppPassword.php +++ b/core/Command/User/AddAppPassword.php @@ -109,8 +109,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 1; } - $output->writeln('The password is not validated so what you provide is what gets recorded in the token'); - + if (!$this->userManager->checkPassword($user->getUID(), $password)) { + $output->writeln('The provided password is invalid'); + return 1; + } $token = $this->random->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS); $generatedToken = $this->tokenProvider->generateToken( From e07b12f1e8e3bf4c9c88ccb5456231e8e8bf45a7 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 14 Mar 2022 13:30:59 +0100 Subject: [PATCH 4/4] Allow creating app passwords without validating the user's password again Aparently the idea behind the command was exactly to do this. Signed-off-by: Joas Schilling --- core/Command/User/AddAppPassword.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/Command/User/AddAppPassword.php b/core/Command/User/AddAppPassword.php index 4f636c406fbaa..7a2270e20b140 100644 --- a/core/Command/User/AddAppPassword.php +++ b/core/Command/User/AddAppPassword.php @@ -109,10 +109,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 1; } - if (!$this->userManager->checkPassword($user->getUID(), $password)) { - $output->writeln('The provided password is invalid'); - return 1; - } + $output->writeln('The password has not been validated, some features might not work as intended.'); $token = $this->random->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS); $generatedToken = $this->tokenProvider->generateToken(