Prechádzať zdrojové kódy

Merge pull request #14263 from nextcloud/publish-activity-app-token

Publish activity if app password created by ocs api or client login flow
Roeland Jago Douma 5 rokov pred
rodič
commit
e7f0e8ba03

+ 12 - 2
core/Controller/AppPasswordController.php

@@ -34,6 +34,8 @@ use OCP\Authentication\LoginCredentials\IStore;
 use OCP\IRequest;
 use OCP\ISession;
 use OCP\Security\ISecureRandom;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\GenericEvent;
 
 class AppPasswordController extends \OCP\AppFramework\OCSController {
 
@@ -49,18 +51,23 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
 	/** @var IStore */
 	private $credentialStore;
 
+	/** @var EventDispatcherInterface */
+	private $eventDispatcher;
+
 	public function __construct(string $appName,
 								IRequest $request,
 								ISession $session,
 								ISecureRandom $random,
 								IProvider $tokenProvider,
-								IStore $credentialStore) {
+								IStore $credentialStore,
+								EventDispatcherInterface $eventDispatcher) {
 		parent::__construct($appName, $request);
 
 		$this->session = $session;
 		$this->random = $random;
 		$this->tokenProvider = $tokenProvider;
 		$this->credentialStore = $credentialStore;
+		$this->eventDispatcher = $eventDispatcher;
 	}
 
 	/**
@@ -91,7 +98,7 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
 
 		$token = $this->random->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
 
-		$this->tokenProvider->generateToken(
+		$generatedToken = $this->tokenProvider->generateToken(
 			$token,
 			$credentials->getUID(),
 			$credentials->getLoginName(),
@@ -101,6 +108,9 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
 			IToken::DO_NOT_REMEMBER
 		);
 
+		$event = new GenericEvent($generatedToken);
+		$this->eventDispatcher->dispatch('app_password_created', $event);
+
 		return new DataResponse([
 			'apppassword' => $token
 		]);

+ 11 - 1
core/Controller/ClientFlowLoginController.php

@@ -46,6 +46,8 @@ use OCP\IUserSession;
 use OCP\Security\ICrypto;
 use OCP\Security\ISecureRandom;
 use OCP\Session\Exceptions\SessionNotAvailableException;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\GenericEvent;
 
 class ClientFlowLoginController extends Controller {
 	/** @var IUserSession */
@@ -68,6 +70,8 @@ class ClientFlowLoginController extends Controller {
 	private $accessTokenMapper;
 	/** @var ICrypto */
 	private $crypto;
+	/** @var EventDispatcherInterface */
+	private $eventDispatcher;
 
 	const stateName = 'client.flow.state.token';
 
@@ -84,6 +88,7 @@ class ClientFlowLoginController extends Controller {
 	 * @param ClientMapper $clientMapper
 	 * @param AccessTokenMapper $accessTokenMapper
 	 * @param ICrypto $crypto
+	 * @param EventDispatcherInterface $eventDispatcher
 	 */
 	public function __construct($appName,
 								IRequest $request,
@@ -96,7 +101,8 @@ class ClientFlowLoginController extends Controller {
 								IURLGenerator $urlGenerator,
 								ClientMapper $clientMapper,
 								AccessTokenMapper $accessTokenMapper,
-								ICrypto $crypto) {
+								ICrypto $crypto,
+								EventDispatcherInterface $eventDispatcher) {
 		parent::__construct($appName, $request);
 		$this->userSession = $userSession;
 		$this->l10n = $l10n;
@@ -108,6 +114,7 @@ class ClientFlowLoginController extends Controller {
 		$this->clientMapper = $clientMapper;
 		$this->accessTokenMapper = $accessTokenMapper;
 		$this->crypto = $crypto;
+		$this->eventDispatcher = $eventDispatcher;
 	}
 
 	/**
@@ -324,6 +331,9 @@ class ClientFlowLoginController extends Controller {
 			$this->tokenProvider->invalidateToken($sessionId);
 		}
 
+		$event = new GenericEvent($generatedToken);
+		$this->eventDispatcher->dispatch('app_password_created', $event);
+
 		return new Http\RedirectResponse($redirectUri);
 	}
 

+ 31 - 0
settings/Application.php

@@ -28,8 +28,10 @@
 
 namespace OC\Settings;
 
+use BadMethodCallException;
 use OC\AppFramework\Utility\TimeFactory;
 use OC\Authentication\Token\IProvider;
+use OC\Authentication\Token\IToken;
 use OC\Server;
 use OC\Settings\Activity\Provider;
 use OC\Settings\Activity\SecurityFilter;
@@ -38,11 +40,15 @@ use OC\Settings\Activity\SecuritySetting;
 use OC\Settings\Activity\Setting;
 use OC\Settings\Mailer\NewUserMailHelper;
 use OC\Settings\Middleware\SubadminMiddleware;
+use OCP\Activity\IManager as IActivityManager;
 use OCP\AppFramework\App;
 use OCP\Defaults;
 use OCP\IContainer;
+use OCP\ILogger;
 use OCP\Settings\IManager;
 use OCP\Util;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\GenericEvent;
 
 /**
  * @package OC\Settings
@@ -109,6 +115,31 @@ class Application extends App {
 				Util::getDefaultEmailAddress('no-reply')
 			);
 		});
+
+		/** @var EventDispatcherInterface $eventDispatcher */
+		$eventDispatcher = $container->getServer()->getEventDispatcher();
+		$eventDispatcher->addListener('app_password_created', function (GenericEvent $event) use ($container) {
+			if (($token = $event->getSubject()) instanceof IToken) {
+				/** @var IActivityManager $activityManager */
+				$activityManager = $container->query(IActivityManager::class);
+				/** @var ILogger $logger */
+				$logger = $container->query(ILogger::class);
+
+				$activity = $activityManager->generateEvent();
+				$activity->setApp('settings')
+					->setType('security')
+					->setAffectedUser($token->getUID())
+					->setAuthor($token->getUID())
+					->setSubject(Provider::APP_TOKEN_CREATED, ['name' => $token->getName()])
+					->setObject('app_token', $token->getId());
+
+				try {
+					$activityManager->publish($activity);
+				} catch (BadMethodCallException $e) {
+					$logger->logException($e, ['message' => 'could not publish activity', 'level' => ILogger::WARN]);
+				}
+			}
+		});
 	}
 
 	public function register() {

+ 13 - 1
tests/Core/Controller/AppPasswordControllerTest.php

@@ -36,6 +36,7 @@ use OCP\IRequest;
 use OCP\ISession;
 use OCP\Security\ISecureRandom;
 use PHPUnit\Framework\MockObject\MockObject;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Test\TestCase;
 
 class AppPasswordControllerTest extends TestCase {
@@ -55,6 +56,9 @@ class AppPasswordControllerTest extends TestCase {
 	/** @var IRequest|MockObject */
 	private $request;
 
+	/** @var EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject */
+	private $eventDispatcher;
+
 	/** @var AppPasswordController */
 	private $controller;
 
@@ -66,6 +70,7 @@ class AppPasswordControllerTest extends TestCase {
 		$this->tokenProvider = $this->createMock(IProvider::class);
 		$this->credentialStore = $this->createMock(IStore::class);
 		$this->request = $this->createMock(IRequest::class);
+		$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
 
 		$this->controller = new AppPasswordController(
 			'core',
@@ -73,7 +78,8 @@ class AppPasswordControllerTest extends TestCase {
 			$this->session,
 			$this->random,
 			$this->tokenProvider,
-			$this->credentialStore
+			$this->credentialStore,
+			$this->eventDispatcher
 		);
 	}
 
@@ -134,6 +140,9 @@ class AppPasswordControllerTest extends TestCase {
 				IToken::DO_NOT_REMEMBER
 			);
 
+		$this->eventDispatcher->expects($this->once())
+			->method('dispatch');
+
 		$this->controller->getAppPassword();
 	}
 
@@ -172,6 +181,9 @@ class AppPasswordControllerTest extends TestCase {
 				IToken::DO_NOT_REMEMBER
 			);
 
+		$this->eventDispatcher->expects($this->once())
+			->method('dispatch');
+
 		$this->controller->getAppPassword();
 	}
 

+ 19 - 1
tests/Core/Controller/ClientFlowLoginControllerTest.php

@@ -41,6 +41,7 @@ use OCP\IUserSession;
 use OCP\Security\ICrypto;
 use OCP\Security\ISecureRandom;
 use OCP\Session\Exceptions\SessionNotAvailableException;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Test\TestCase;
 
 class ClientFlowLoginControllerTest extends TestCase {
@@ -66,6 +67,9 @@ class ClientFlowLoginControllerTest extends TestCase {
 	private $accessTokenMapper;
 	/** @var ICrypto|\PHPUnit_Framework_MockObject_MockObject */
 	private $crypto;
+	/** @var EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject */
+	private $eventDispatcher;
+
 
 	/** @var ClientFlowLoginController */
 	private $clientFlowLoginController;
@@ -90,6 +94,7 @@ class ClientFlowLoginControllerTest extends TestCase {
 		$this->clientMapper = $this->createMock(ClientMapper::class);
 		$this->accessTokenMapper = $this->createMock(AccessTokenMapper::class);
 		$this->crypto = $this->createMock(ICrypto::class);
+		$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
 
 		$this->clientFlowLoginController = new ClientFlowLoginController(
 			'core',
@@ -103,7 +108,8 @@ class ClientFlowLoginControllerTest extends TestCase {
 			$this->urlGenerator,
 			$this->clientMapper,
 			$this->accessTokenMapper,
-			$this->crypto
+			$this->crypto,
+			$this->eventDispatcher
 		);
 	}
 
@@ -378,6 +384,9 @@ class ClientFlowLoginControllerTest extends TestCase {
 			->method('getHeader')
 			->willReturn('');
 
+		$this->eventDispatcher->expects($this->once())
+			->method('dispatch');
+
 		$expected = new Http\RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken');
 		$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
 	}
@@ -462,6 +471,9 @@ class ClientFlowLoginControllerTest extends TestCase {
 			->with('MyClientIdentifier')
 			->willReturn($client);
 
+		$this->eventDispatcher->expects($this->once())
+			->method('dispatch');
+
 		$expected = new Http\RedirectResponse('https://example.com/redirect.php?state=MyOauthState&code=MyAccessCode');
 		$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken', 'MyClientIdentifier'));
 	}
@@ -534,6 +546,9 @@ class ClientFlowLoginControllerTest extends TestCase {
 			->method('getHeader')
 			->willReturn('');
 
+		$this->eventDispatcher->expects($this->once())
+			->method('dispatch');
+
 		$expected = new Http\RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken');
 		$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
 	}
@@ -662,6 +677,9 @@ class ClientFlowLoginControllerTest extends TestCase {
 			->method('getHeader')
 			->willReturnMap($headers);
 
+		$this->eventDispatcher->expects($this->once())
+			->method('dispatch');
+
 		$expected = new Http\RedirectResponse('nc://login/server:' . $expected . '://example.com&user:MyLoginName&password:MyGeneratedToken');
 		$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
 	}