Skip to content

Commit

Permalink
[stable10] add IAccountModules and AccountModuleMiddleware
Browse files Browse the repository at this point in the history
Signed-off-by: Jörn Friedrich Dreyer <[email protected]>
  • Loading branch information
butonic authored and mrow4a committed Jun 25, 2018
1 parent 561f110 commit 7dc73ea
Show file tree
Hide file tree
Showing 18 changed files with 770 additions and 32 deletions.
1 change: 1 addition & 0 deletions apps/dav/appinfo/v1/caldav.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
\OC::$server->getUserSession(),
\OC::$server->getRequest(),
\OC::$server->getTwoFactorAuthManager(),
\OC::$server->getAccountModuleManager(),
'principals/'
);
$principalBackend = new Principal(
Expand Down
1 change: 1 addition & 0 deletions apps/dav/appinfo/v1/carddav.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
\OC::$server->getUserSession(),
\OC::$server->getRequest(),
\OC::$server->getTwoFactorAuthManager(),
\OC::$server->getAccountModuleManager(),
'principals/'
);
$principalBackend = new Principal(
Expand Down
1 change: 1 addition & 0 deletions apps/dav/appinfo/v1/webdav.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
\OC::$server->getUserSession(),
\OC::$server->getRequest(),
\OC::$server->getTwoFactorAuthManager(),
\OC::$server->getAccountModuleManager(),
'principals/'
);
$requestUri = \OC::$server->getRequest()->getRequestUri();
Expand Down
42 changes: 34 additions & 8 deletions apps/dav/lib/Connector/Sabre/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@

use Exception;
use OC\AppFramework\Http\Request;
use OC\Authentication\Exceptions\AccountCheckException;
use OC\Authentication\Exceptions\PasswordLoginForbiddenException;
use OC\Authentication\TwoFactorAuth\Manager;
use OC\Authentication\AccountModule\Manager as AccountModuleManager;
use OC\User\LoginException;
use OC\User\Session;
use OCA\DAV\Connector\Sabre\Exception\PasswordLoginForbidden;
Expand All @@ -59,22 +61,27 @@ class Auth extends AbstractBasic {
private $currentUser;
/** @var Manager */
private $twoFactorManager;
/** @var AccountModuleManager */
private $accountModuleManager;

/**
* @param ISession $session
* @param Session $userSession
* @param IRequest $request
* @param Manager $twoFactorManager
* @param AccountModuleManager $accountModuleManager
* @param string $principalPrefix
*/
public function __construct(ISession $session,
Session $userSession,
IRequest $request,
Manager $twoFactorManager,
AccountModuleManager $accountModuleManager,
$principalPrefix = 'principals/users/') {
$this->session = $session;
$this->userSession = $userSession;
$this->twoFactorManager = $twoFactorManager;
$this->accountModuleManager = $accountModuleManager;
$this->request = $request;
$this->principalPrefix = $principalPrefix;

Expand Down Expand Up @@ -200,6 +207,7 @@ private function requiresCSRFCheck() {
* @param ResponseInterface $response
* @return array
* @throws NotAuthenticated
* @throws ServiceUnavailable
*/
private function auth(RequestInterface $request, ResponseInterface $response) {
$forcedLogout = false;
Expand All @@ -221,11 +229,13 @@ private function auth(RequestInterface $request, ResponseInterface $response) {
//Well behaved clients that only send the cookie are allowed
($this->userSession->isLoggedIn() && $this->session->get(self::DAV_AUTHENTICATED) === $this->userSession->getUser()->getUID() && $request->getHeader('Authorization') === null)
) {
$user = $this->userSession->getUser()->getUID();
\OC_Util::setupFS($user);
$this->currentUser = $user;
$user = $this->userSession->getUser();
$this->checkAccountModule($user);
$uid = $user->getUID();
\OC_Util::setupFS($uid);
$this->currentUser = $uid;
$this->session->close();
return [true, $this->principalPrefix . $user];
return [true, $this->principalPrefix . $uid];
}
}

Expand All @@ -237,11 +247,27 @@ private function auth(RequestInterface $request, ResponseInterface $response) {
}

$data = parent::check($request, $response);
if($data[0] === true) {
$startPos = strrpos($data[1], '/') + 1;
$user = $this->userSession->getUser()->getUID();
$data[1] = substr_replace($data[1], $user, $startPos);
if ($data[0] === true) {
$user = $this->userSession->getUser();
$this->checkAccountModule($user);
$startPos = \strrpos($data[1], '/') + 1;
$data[1] = \substr_replace($data[1], $user->getUID(), $startPos);
}
return $data;
}

/**
* @param $user
* @throws ServiceUnavailable
*/
private function checkAccountModule($user) {
if ($user === null) {
throw new \UnexpectedValueException('No user in session');
}
try {
$this->accountModuleManager->check($user);
} catch (AccountCheckException $ex) {
throw new ServiceUnavailable($ex->getMessage(), $ex->getCode(), $ex);
}
}
}
3 changes: 2 additions & 1 deletion apps/dav/lib/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ public function __construct(IRequest $request, $baseUri) {
\OC::$server->getSession(),
\OC::$server->getUserSession(),
\OC::$server->getRequest(),
\OC::$server->getTwoFactorAuthManager()
\OC::$server->getTwoFactorAuthManager(),
\OC::$server->getAccountModuleManager()
);

// Set URL explicitly due to reverse-proxy situations
Expand Down
22 changes: 10 additions & 12 deletions apps/dav/tests/unit/Connector/Sabre/AuthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@

use OC\Authentication\Exceptions\PasswordLoginForbiddenException;
use OC\Authentication\TwoFactorAuth\Manager;
use OC\ForbiddenException;
use OC\Authentication\AccountModule\Manager as AccountModuleManager;
use OC\User\LoginException;
use OC\User\Session;
use OCA\DAV\Connector\Sabre\Auth;
use OCP\IRequest;
use OCP\ISession;
use OCP\IUser;
use Sabre\DAV\Exception\NotAuthenticated;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
use Test\TestCase;
Expand All @@ -58,23 +57,22 @@ class AuthTest extends TestCase {
private $request;
/** @var Manager | \PHPUnit_Framework_MockObject_MockObject */
private $twoFactorManager;
/** @var AccountModuleManager | \PHPUnit_Framework_MockObject_MockObject */
private $accountModuleManager;

public function setUp() {
parent::setUp();
$this->session = $this->getMockBuilder(ISession::class)
->disableOriginalConstructor()->getMock();
$this->userSession = $this->getMockBuilder(Session::class)
->disableOriginalConstructor()->getMock();
$this->request = $this->getMockBuilder(IRequest::class)
->disableOriginalConstructor()->getMock();
$this->twoFactorManager = $this->getMockBuilder(Manager::class)
->disableOriginalConstructor()
->getMock();
$this->session = $this->createMock(ISession::class);
$this->userSession = $this->createMock(Session::class);
$this->request = $this->createMock(IRequest::class);
$this->twoFactorManager = $this->createMock(Manager::class);
$this->accountModuleManager = $this->createMock(AccountModuleManager::class);
$this->auth = new Auth(
$this->session,
$this->userSession,
$this->request,
$this->twoFactorManager
$this->twoFactorManager,
$this->accountModuleManager
);
}

Expand Down
124 changes: 124 additions & 0 deletions core/Middleware/AccountModuleMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php
/**
* @author Jörn Friedrich Dreyer <[email protected]>
*
* @copyright Copyright (c) 2018, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OC\Core\Middleware;

use Exception;
use OC\Authentication\AccountModule\Manager;
use OC\Authentication\Exceptions\AccountCheckException;
use OC\Core\Controller\LoginController;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Middleware;
use OCP\AppFramework\Utility\IControllerMethodReflector;
use OCP\Authentication\IAccountModuleController;
use OCP\ILogger;
use OCP\IUserSession;

/**
* Class AccountModuleMiddleware
*
* Apps can register IAccountModules in their info.xml. These IAccountModules
* will be checked on every request. So the check() call should be cheap.
*
* @package OC\Core\Middleware
*/
class AccountModuleMiddleware extends Middleware {

/** @var ILogger */
private $logger;

/** @var Manager */
private $manager;

/** @var IUserSession */
private $session;

/** @var IControllerMethodReflector */
private $reflector;

/**
* @param ILogger $logger
* @param Manager $manager
* @param IUserSession $session
* @param IControllerMethodReflector $reflector
*/
public function __construct(
ILogger $logger,
Manager $manager,
IUserSession $session,
IControllerMethodReflector $reflector
) {
$this->logger = $logger;
$this->manager = $manager;
$this->session = $session;
$this->reflector = $reflector;
}

/**
* @param Controller $controller
* @param string $methodName
* @throws AccountCheckException
*/
public function beforeController($controller, $methodName) {
if ($this->reflector->hasAnnotation('PublicPage')) {
// Don't block public pages
return;
}

if ($controller instanceof LoginController && $methodName === 'logout') {
// Don't block the logout page
return;
}

if ($controller instanceof IAccountModuleController) {
// Don't block any IAccountModuleController controllers
return;
}

if ($this->session->isLoggedIn()) {
$user = $this->session->getUser();
if ($user === null) {
throw new \UnexpectedValueException('User isLoggedIn but session does not contain user');
}
$this->manager->check($user);
}
}

/**
* @param Controller $controller
* @param string $methodName
* @param Exception $exception
* @return Http\Response
* @throws Exception
*/
public function afterException($controller, $methodName, Exception $exception) {
if ($exception instanceof AccountCheckException) {
$this->logger->debug('IAccountModule check failed: {message}, {code}', [
'app'=>__METHOD__,
'message' => $exception->getMessage(),
'code' => $exception->getCode()
]);
return $exception->getRedirectResponse();
}
throw $exception;
}
}
6 changes: 5 additions & 1 deletion lib/private/AppFramework/DependencyInjection/DIContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
use OC\AppFramework\Middleware\Security\SecurityMiddleware;
use OC\AppFramework\Middleware\SessionMiddleware;
use OC\AppFramework\Utility\SimpleContainer;
use OC\Core\Middleware\AccountModuleMiddleware;
use OC\Core\Middleware\TwoFactorMiddleware;
use OCP\App\IServiceLoader;
use OCP\AppFramework\IApi;
use OCP\AppFramework\IAppContainer;
use OCP\Files\Mount\IMountManager;
Expand Down Expand Up @@ -284,6 +286,7 @@ public function __construct($appName, $urlParams = []){
return $this->getServer();
});
$this->registerAlias('OCP\\IServerContainer', 'ServerContainer');
$this->registerAlias(IServiceLoader::class, 'ServerContainer');

$this->registerService('Symfony\Component\EventDispatcher\EventDispatcherInterface', function ($c) {
return $this->getServer()->getEventDispatcher();
Expand Down Expand Up @@ -391,7 +394,8 @@ public function __construct($appName, $urlParams = []){
$dispatcher = new MiddlewareDispatcher();
$dispatcher->registerMiddleware($c['CORSMiddleware']);
$dispatcher->registerMiddleware($c['SecurityMiddleware']);
$dispatcher->registerMiddleWare($c['TwoFactorMiddleware']);
$dispatcher->registerMiddleware($c['TwoFactorMiddleware']);
$dispatcher->registerMiddleware($c->query(AccountModuleMiddleware::class));

foreach($middleWares as $middleWare) {
$dispatcher->registerMiddleware($c[$middleWare]);
Expand Down
Loading

0 comments on commit 7dc73ea

Please sign in to comment.