Emit event if app password created

Signed-off-by: Daniel Kesselberg <mail@danielkesselberg.de>
This commit is contained in:
Daniel Kesselberg 2019-02-18 17:38:38 +01:00
parent 149a98edf6
commit c583c5e7e2
No known key found for this signature in database
GPG key ID: 36E3664E099D0614
5 changed files with 72 additions and 106 deletions

View file

@ -24,20 +24,18 @@ declare(strict_types=1);
namespace OC\Core\Controller; namespace OC\Core\Controller;
use BadMethodCallException;
use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IProvider;
use OC\Authentication\Token\IToken; use OC\Authentication\Token\IToken;
use OC\Settings\Activity\Provider;
use OCP\Activity\IManager as IActivityManager;
use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSForbiddenException; use OCP\AppFramework\OCS\OCSForbiddenException;
use OCP\Authentication\Exceptions\CredentialsUnavailableException; use OCP\Authentication\Exceptions\CredentialsUnavailableException;
use OCP\Authentication\Exceptions\PasswordUnavailableException; use OCP\Authentication\Exceptions\PasswordUnavailableException;
use OCP\Authentication\LoginCredentials\IStore; use OCP\Authentication\LoginCredentials\IStore;
use OCP\ILogger;
use OCP\IRequest; use OCP\IRequest;
use OCP\ISession; use OCP\ISession;
use OCP\Security\ISecureRandom; use OCP\Security\ISecureRandom;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
class AppPasswordController extends \OCP\AppFramework\OCSController { class AppPasswordController extends \OCP\AppFramework\OCSController {
@ -50,31 +48,26 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
/** @var IProvider */ /** @var IProvider */
private $tokenProvider; private $tokenProvider;
/** @var IActivityManager */
private $activityManager;
/** @var ILogger */
private $logger;
/** @var IStore */ /** @var IStore */
private $credentialStore; private $credentialStore;
/** @var EventDispatcherInterface */
private $eventDispatcher;
public function __construct(string $appName, public function __construct(string $appName,
IRequest $request, IRequest $request,
ISession $session, ISession $session,
ISecureRandom $random, ISecureRandom $random,
IProvider $tokenProvider, IProvider $tokenProvider,
IStore $credentialStore, IStore $credentialStore,
IActivityManager $activityManager, EventDispatcherInterface $eventDispatcher) {
ILogger $logger) {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->session = $session; $this->session = $session;
$this->random = $random; $this->random = $random;
$this->tokenProvider = $tokenProvider; $this->tokenProvider = $tokenProvider;
$this->credentialStore = $credentialStore; $this->credentialStore = $credentialStore;
$this->activityManager = $activityManager; $this->eventDispatcher = $eventDispatcher;
$this->logger = $logger;
} }
/** /**
@ -115,20 +108,8 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
IToken::DO_NOT_REMEMBER IToken::DO_NOT_REMEMBER
); );
$event = $this->activityManager->generateEvent(); $event = new GenericEvent($generatedToken);
$event->setApp('settings') $this->eventDispatcher->dispatch('app_password_created', $event);
->setType('security')
->setAffectedUser($credentials->getUID())
->setAuthor($credentials->getUID())
->setSubject(Provider::APP_TOKEN_CREATED, ['name' => $generatedToken->getName()])
->setObject('app_token', $generatedToken->getId(), 'App Password');
try {
$this->activityManager->publish($event);
} catch (BadMethodCallException $e) {
$this->logger->warning('could not publish activity');
$this->logger->logException($e);
}
return new DataResponse([ return new DataResponse([
'apppassword' => $token 'apppassword' => $token

View file

@ -26,23 +26,19 @@
namespace OC\Core\Controller; namespace OC\Core\Controller;
use BadMethodCallException;
use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException;
use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IProvider;
use OC\Authentication\Token\IToken; use OC\Authentication\Token\IToken;
use OC\Settings\Activity\Provider;
use OCA\OAuth2\Db\AccessToken; use OCA\OAuth2\Db\AccessToken;
use OCA\OAuth2\Db\AccessTokenMapper; use OCA\OAuth2\Db\AccessTokenMapper;
use OCA\OAuth2\Db\ClientMapper; use OCA\OAuth2\Db\ClientMapper;
use OCP\Activity\IManager as IActivityManager;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http; use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\StandaloneTemplateResponse; use OCP\AppFramework\Http\StandaloneTemplateResponse;
use OCP\Defaults; use OCP\Defaults;
use OCP\IL10N; use OCP\IL10N;
use OCP\ILogger;
use OCP\IRequest; use OCP\IRequest;
use OCP\ISession; use OCP\ISession;
use OCP\IURLGenerator; use OCP\IURLGenerator;
@ -50,6 +46,8 @@ use OCP\IUserSession;
use OCP\Security\ICrypto; use OCP\Security\ICrypto;
use OCP\Security\ISecureRandom; use OCP\Security\ISecureRandom;
use OCP\Session\Exceptions\SessionNotAvailableException; use OCP\Session\Exceptions\SessionNotAvailableException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
class ClientFlowLoginController extends Controller { class ClientFlowLoginController extends Controller {
/** @var IUserSession */ /** @var IUserSession */
@ -72,10 +70,8 @@ class ClientFlowLoginController extends Controller {
private $accessTokenMapper; private $accessTokenMapper;
/** @var ICrypto */ /** @var ICrypto */
private $crypto; private $crypto;
/** @var IActivityManager */ /** @var EventDispatcherInterface */
private $activityManager; private $eventDispatcher;
/** @var ILogger */
private $logger;
const stateName = 'client.flow.state.token'; const stateName = 'client.flow.state.token';
@ -92,8 +88,7 @@ class ClientFlowLoginController extends Controller {
* @param ClientMapper $clientMapper * @param ClientMapper $clientMapper
* @param AccessTokenMapper $accessTokenMapper * @param AccessTokenMapper $accessTokenMapper
* @param ICrypto $crypto * @param ICrypto $crypto
* @param IActivityManager $activityManager * @param EventDispatcherInterface $eventDispatcher
* @param ILogger $logger
*/ */
public function __construct($appName, public function __construct($appName,
IRequest $request, IRequest $request,
@ -107,8 +102,7 @@ class ClientFlowLoginController extends Controller {
ClientMapper $clientMapper, ClientMapper $clientMapper,
AccessTokenMapper $accessTokenMapper, AccessTokenMapper $accessTokenMapper,
ICrypto $crypto, ICrypto $crypto,
IActivityManager $activityManager, EventDispatcherInterface $eventDispatcher) {
ILogger $logger) {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->userSession = $userSession; $this->userSession = $userSession;
$this->l10n = $l10n; $this->l10n = $l10n;
@ -120,8 +114,7 @@ class ClientFlowLoginController extends Controller {
$this->clientMapper = $clientMapper; $this->clientMapper = $clientMapper;
$this->accessTokenMapper = $accessTokenMapper; $this->accessTokenMapper = $accessTokenMapper;
$this->crypto = $crypto; $this->crypto = $crypto;
$this->activityManager = $activityManager; $this->eventDispatcher = $eventDispatcher;
$this->logger = $logger;
} }
/** /**
@ -338,20 +331,8 @@ class ClientFlowLoginController extends Controller {
$this->tokenProvider->invalidateToken($sessionId); $this->tokenProvider->invalidateToken($sessionId);
} }
$event = $this->activityManager->generateEvent(); $event = new GenericEvent($generatedToken);
$event->setApp('settings') $this->eventDispatcher->dispatch('app_password_created', $event);
->setType('security')
->setAffectedUser($uid)
->setAuthor($uid)
->setSubject(Provider::APP_TOKEN_CREATED, ['name' => $generatedToken->getName()])
->setObject('app_token', $generatedToken->getId(), 'App Password');
try {
$this->activityManager->publish($event);
} catch (BadMethodCallException $e) {
$this->logger->warning('could not publish activity');
$this->logger->logException($e);
}
return new Http\RedirectResponse($redirectUri); return new Http\RedirectResponse($redirectUri);
} }

View file

@ -28,8 +28,10 @@
namespace OC\Settings; namespace OC\Settings;
use BadMethodCallException;
use OC\AppFramework\Utility\TimeFactory; use OC\AppFramework\Utility\TimeFactory;
use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IProvider;
use OC\Authentication\Token\IToken;
use OC\Server; use OC\Server;
use OC\Settings\Activity\Provider; use OC\Settings\Activity\Provider;
use OC\Settings\Activity\SecurityFilter; use OC\Settings\Activity\SecurityFilter;
@ -38,11 +40,15 @@ use OC\Settings\Activity\SecuritySetting;
use OC\Settings\Activity\Setting; use OC\Settings\Activity\Setting;
use OC\Settings\Mailer\NewUserMailHelper; use OC\Settings\Mailer\NewUserMailHelper;
use OC\Settings\Middleware\SubadminMiddleware; use OC\Settings\Middleware\SubadminMiddleware;
use OCP\Activity\IManager as IActivityManager;
use OCP\AppFramework\App; use OCP\AppFramework\App;
use OCP\Defaults; use OCP\Defaults;
use OCP\IContainer; use OCP\IContainer;
use OCP\ILogger;
use OCP\Settings\IManager; use OCP\Settings\IManager;
use OCP\Util; use OCP\Util;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
/** /**
* @package OC\Settings * @package OC\Settings
@ -109,6 +115,31 @@ class Application extends App {
Util::getDefaultEmailAddress('no-reply') 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() { public function register() {

View file

@ -27,18 +27,16 @@ namespace Tests\Core\Controller;
use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IProvider;
use OC\Authentication\Token\IToken; use OC\Authentication\Token\IToken;
use OC\Core\Controller\AppPasswordController; use OC\Core\Controller\AppPasswordController;
use OCP\Activity\IEvent;
use OCP\Activity\IManager as IActivityManager;
use OCP\AppFramework\OCS\OCSForbiddenException; use OCP\AppFramework\OCS\OCSForbiddenException;
use OCP\Authentication\Exceptions\CredentialsUnavailableException; use OCP\Authentication\Exceptions\CredentialsUnavailableException;
use OCP\Authentication\Exceptions\PasswordUnavailableException; use OCP\Authentication\Exceptions\PasswordUnavailableException;
use OCP\Authentication\LoginCredentials\ICredentials; use OCP\Authentication\LoginCredentials\ICredentials;
use OCP\Authentication\LoginCredentials\IStore; use OCP\Authentication\LoginCredentials\IStore;
use OCP\ILogger;
use OCP\IRequest; use OCP\IRequest;
use OCP\ISession; use OCP\ISession;
use OCP\Security\ISecureRandom; use OCP\Security\ISecureRandom;
use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Test\TestCase; use Test\TestCase;
class AppPasswordControllerTest extends TestCase { class AppPasswordControllerTest extends TestCase {
@ -58,8 +56,8 @@ class AppPasswordControllerTest extends TestCase {
/** @var IRequest|MockObject */ /** @var IRequest|MockObject */
private $request; private $request;
/** @var IActivityManager|\PHPUnit_Framework_MockObject_MockObject */ /** @var EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject */
private $activityManager; private $eventDispatcher;
/** @var AppPasswordController */ /** @var AppPasswordController */
private $controller; private $controller;
@ -72,9 +70,7 @@ class AppPasswordControllerTest extends TestCase {
$this->tokenProvider = $this->createMock(IProvider::class); $this->tokenProvider = $this->createMock(IProvider::class);
$this->credentialStore = $this->createMock(IStore::class); $this->credentialStore = $this->createMock(IStore::class);
$this->request = $this->createMock(IRequest::class); $this->request = $this->createMock(IRequest::class);
$this->activityManager = $this->createMock(IActivityManager::class); $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
/** @var ILogger|\PHPUnit_Framework_MockObject_MockObject $logger */
$logger = $this->createMock(ILogger::class);
$this->controller = new AppPasswordController( $this->controller = new AppPasswordController(
'core', 'core',
@ -83,8 +79,7 @@ class AppPasswordControllerTest extends TestCase {
$this->random, $this->random,
$this->tokenProvider, $this->tokenProvider,
$this->credentialStore, $this->credentialStore,
$this->activityManager, $this->eventDispatcher
$logger
); );
} }
@ -145,11 +140,8 @@ class AppPasswordControllerTest extends TestCase {
IToken::DO_NOT_REMEMBER IToken::DO_NOT_REMEMBER
); );
$this->activityManager->expects($this->once()) $this->eventDispatcher->expects($this->once())
->method('generateEvent') ->method('dispatch');
->willReturn($this->createMock(IEvent::class));
$this->activityManager->expects($this->once())
->method('publish');
$this->controller->getAppPassword(); $this->controller->getAppPassword();
} }
@ -189,11 +181,8 @@ class AppPasswordControllerTest extends TestCase {
IToken::DO_NOT_REMEMBER IToken::DO_NOT_REMEMBER
); );
$this->activityManager->expects($this->once()) $this->eventDispatcher->expects($this->once())
->method('generateEvent') ->method('dispatch');
->willReturn($this->createMock(IEvent::class));
$this->activityManager->expects($this->once())
->method('publish');
$this->controller->getAppPassword(); $this->controller->getAppPassword();
} }

View file

@ -29,13 +29,10 @@ use OC\Core\Controller\ClientFlowLoginController;
use OCA\OAuth2\Db\AccessTokenMapper; use OCA\OAuth2\Db\AccessTokenMapper;
use OCA\OAuth2\Db\Client; use OCA\OAuth2\Db\Client;
use OCA\OAuth2\Db\ClientMapper; use OCA\OAuth2\Db\ClientMapper;
use OCP\Activity\IEvent;
use OCP\Activity\IManager as IActivityManager;
use OCP\AppFramework\Http; use OCP\AppFramework\Http;
use OCP\AppFramework\Http\StandaloneTemplateResponse; use OCP\AppFramework\Http\StandaloneTemplateResponse;
use OCP\Defaults; use OCP\Defaults;
use OCP\IL10N; use OCP\IL10N;
use OCP\ILogger;
use OCP\IRequest; use OCP\IRequest;
use OCP\ISession; use OCP\ISession;
use OCP\IURLGenerator; use OCP\IURLGenerator;
@ -44,6 +41,7 @@ use OCP\IUserSession;
use OCP\Security\ICrypto; use OCP\Security\ICrypto;
use OCP\Security\ISecureRandom; use OCP\Security\ISecureRandom;
use OCP\Session\Exceptions\SessionNotAvailableException; use OCP\Session\Exceptions\SessionNotAvailableException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Test\TestCase; use Test\TestCase;
class ClientFlowLoginControllerTest extends TestCase { class ClientFlowLoginControllerTest extends TestCase {
@ -69,8 +67,9 @@ class ClientFlowLoginControllerTest extends TestCase {
private $accessTokenMapper; private $accessTokenMapper;
/** @var ICrypto|\PHPUnit_Framework_MockObject_MockObject */ /** @var ICrypto|\PHPUnit_Framework_MockObject_MockObject */
private $crypto; private $crypto;
/** @var IActivityManager|\PHPUnit_Framework_MockObject_MockObject */ /** @var EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject */
private $activityManager; private $eventDispatcher;
/** @var ClientFlowLoginController */ /** @var ClientFlowLoginController */
private $clientFlowLoginController; private $clientFlowLoginController;
@ -95,9 +94,7 @@ class ClientFlowLoginControllerTest extends TestCase {
$this->clientMapper = $this->createMock(ClientMapper::class); $this->clientMapper = $this->createMock(ClientMapper::class);
$this->accessTokenMapper = $this->createMock(AccessTokenMapper::class); $this->accessTokenMapper = $this->createMock(AccessTokenMapper::class);
$this->crypto = $this->createMock(ICrypto::class); $this->crypto = $this->createMock(ICrypto::class);
$this->activityManager = $this->createMock(IActivityManager::class); $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
/** @var ILogger|\PHPUnit_Framework_MockObject_MockObject $logger */
$logger = $this->createMock(ILogger::class);
$this->clientFlowLoginController = new ClientFlowLoginController( $this->clientFlowLoginController = new ClientFlowLoginController(
'core', 'core',
@ -112,8 +109,7 @@ class ClientFlowLoginControllerTest extends TestCase {
$this->clientMapper, $this->clientMapper,
$this->accessTokenMapper, $this->accessTokenMapper,
$this->crypto, $this->crypto,
$this->activityManager, $this->eventDispatcher
$logger
); );
} }
@ -388,11 +384,8 @@ class ClientFlowLoginControllerTest extends TestCase {
->method('getHeader') ->method('getHeader')
->willReturn(''); ->willReturn('');
$this->activityManager->expects($this->once()) $this->eventDispatcher->expects($this->once())
->method('generateEvent') ->method('dispatch');
->willReturn($this->createMock(IEvent::class));
$this->activityManager->expects($this->once())
->method('publish');
$expected = new Http\RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken'); $expected = new Http\RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken');
$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken')); $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
@ -478,11 +471,8 @@ class ClientFlowLoginControllerTest extends TestCase {
->with('MyClientIdentifier') ->with('MyClientIdentifier')
->willReturn($client); ->willReturn($client);
$this->activityManager->expects($this->once()) $this->eventDispatcher->expects($this->once())
->method('generateEvent') ->method('dispatch');
->willReturn($this->createMock(IEvent::class));
$this->activityManager->expects($this->once())
->method('publish');
$expected = new Http\RedirectResponse('https://example.com/redirect.php?state=MyOauthState&code=MyAccessCode'); $expected = new Http\RedirectResponse('https://example.com/redirect.php?state=MyOauthState&code=MyAccessCode');
$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken', 'MyClientIdentifier')); $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken', 'MyClientIdentifier'));
@ -556,11 +546,8 @@ class ClientFlowLoginControllerTest extends TestCase {
->method('getHeader') ->method('getHeader')
->willReturn(''); ->willReturn('');
$this->activityManager->expects($this->once()) $this->eventDispatcher->expects($this->once())
->method('generateEvent') ->method('dispatch');
->willReturn($this->createMock(IEvent::class));
$this->activityManager->expects($this->once())
->method('publish');
$expected = new Http\RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken'); $expected = new Http\RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken');
$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken')); $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
@ -690,11 +677,8 @@ class ClientFlowLoginControllerTest extends TestCase {
->method('getHeader') ->method('getHeader')
->willReturnMap($headers); ->willReturnMap($headers);
$this->activityManager->expects($this->once()) $this->eventDispatcher->expects($this->once())
->method('generateEvent') ->method('dispatch');
->willReturn($this->createMock(IEvent::class));
$this->activityManager->expects($this->once())
->method('publish');
$expected = new Http\RedirectResponse('nc://login/server:' . $expected . '://example.com&user:MyLoginName&password:MyGeneratedToken'); $expected = new Http\RedirectResponse('nc://login/server:' . $expected . '://example.com&user:MyLoginName&password:MyGeneratedToken');
$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken')); $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));