diff options
author | Christoph Wurst <christoph@owncloud.com> | 2016-05-11 11:23:25 +0200 |
---|---|---|
committer | Christoph Wurst <christoph@owncloud.com> | 2016-05-23 11:21:10 +0200 |
commit | dfb4d426c24c8cbb7e207a3dd92b5fcd894a1977 (patch) | |
tree | dc640b6bb84d032a6a45ca03ffe91e37d9c99ea9 /lib | |
parent | dec3f9ebcbdeacf5bc483df93900b157a1a5e546 (diff) | |
download | nextcloud-server-dfb4d426c24c8cbb7e207a3dd92b5fcd894a1977.tar.gz nextcloud-server-dfb4d426c24c8cbb7e207a3dd92b5fcd894a1977.zip |
Add two factor auth to core
Diffstat (limited to 'lib')
-rw-r--r-- | lib/private/App/AppManager.php | 2 | ||||
-rw-r--r-- | lib/private/App/InfoParser.php | 3 | ||||
-rw-r--r-- | lib/private/AppFramework/DependencyInjection/DIContainer.php | 15 | ||||
-rw-r--r-- | lib/private/Authentication/Exceptions/LoginRequiredException.php | 29 | ||||
-rw-r--r-- | lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php | 29 | ||||
-rw-r--r-- | lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php | 29 | ||||
-rw-r--r-- | lib/private/Authentication/TwoFactorAuth/Manager.php | 141 | ||||
-rw-r--r-- | lib/private/Server.php | 12 | ||||
-rw-r--r-- | lib/private/User/Session.php | 1 | ||||
-rw-r--r-- | lib/public/Authentication/TwoFactorAuth/IProvider.php | 93 |
10 files changed, 351 insertions, 3 deletions
diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 69e5334774e..37cfb83012c 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -301,7 +301,7 @@ class AppManager implements IAppManager { * * @param string $appId app id * - * @return array app iinfo + * @return array app info * * @internal */ diff --git a/lib/private/App/InfoParser.php b/lib/private/App/InfoParser.php index e9456550206..7e6e5df5411 100644 --- a/lib/private/App/InfoParser.php +++ b/lib/private/App/InfoParser.php @@ -92,6 +92,9 @@ class InfoParser { if (!array_key_exists('background-jobs', $array)) { $array['background-jobs'] = []; } + if (!array_key_exists('two-factor-providers', $array)) { + $array['two-factor-providers'] = []; + } if (array_key_exists('documentation', $array) && is_array($array['documentation'])) { foreach ($array['documentation'] as $key => $url) { diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php index 439b631b50f..e120479ac59 100644 --- a/lib/private/AppFramework/DependencyInjection/DIContainer.php +++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php @@ -31,15 +31,16 @@ namespace OC\AppFramework\DependencyInjection; use OC; +use OC\AppFramework\Core\API; use OC\AppFramework\Http; use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\Http\Output; -use OC\AppFramework\Core\API; use OC\AppFramework\Middleware\MiddlewareDispatcher; -use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Middleware\Security\CORSMiddleware; +use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Middleware\SessionMiddleware; use OC\AppFramework\Utility\SimpleContainer; +use OC\Core\Middleware\TwoFactorMiddleware; use OCP\AppFramework\IApi; use OCP\AppFramework\IAppContainer; @@ -357,11 +358,21 @@ class DIContainer extends SimpleContainer implements IAppContainer { ); }); + $this->registerService('TwoFactorMiddleware', function (SimpleContainer $c) use ($app) { + $twoFactorManager = $c->getServer()->getTwoFactorAuthManager(); + $userSession = $app->getServer()->getUserSession(); + $session = $app->getServer()->getSession(); + $urlGenerator = $app->getServer()->getURLGenerator(); + $reflector = $c['ControllerMethodReflector']; + return new TwoFactorMiddleware($twoFactorManager, $userSession, $session, $urlGenerator, $reflector); + }); + $middleWares = &$this->middleWares; $this->registerService('MiddlewareDispatcher', function($c) use (&$middleWares) { $dispatcher = new MiddlewareDispatcher(); $dispatcher->registerMiddleware($c['CORSMiddleware']); $dispatcher->registerMiddleware($c['SecurityMiddleware']); + $dispatcher->registerMiddleWare($c['TwoFactorMiddleware']); foreach($middleWares as $middleWare) { $dispatcher->registerMiddleware($c[$middleWare]); diff --git a/lib/private/Authentication/Exceptions/LoginRequiredException.php b/lib/private/Authentication/Exceptions/LoginRequiredException.php new file mode 100644 index 00000000000..1c388cc1e3f --- /dev/null +++ b/lib/private/Authentication/Exceptions/LoginRequiredException.php @@ -0,0 +1,29 @@ +<?php + +/** + * @author Christoph Wurst <christoph@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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\Authentication\Exceptions; + +use Exception; + +class LoginRequiredException extends Exception { + +} diff --git a/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php b/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php new file mode 100644 index 00000000000..dc9f8937504 --- /dev/null +++ b/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php @@ -0,0 +1,29 @@ +<?php + +/** + * @author Christoph Wurst <christoph@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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\Authentication\Exceptions; + +use Exception; + +class TwoFactorAuthRequiredException extends Exception { + +} diff --git a/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php b/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php new file mode 100644 index 00000000000..28a46323f02 --- /dev/null +++ b/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php @@ -0,0 +1,29 @@ +<?php + +/** + * @author Christoph Wurst <christoph@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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\Authentication\Exceptions; + +use Exception; + +class UserAlreadyLoggedInException extends Exception { + +} diff --git a/lib/private/Authentication/TwoFactorAuth/Manager.php b/lib/private/Authentication/TwoFactorAuth/Manager.php new file mode 100644 index 00000000000..2fdadee6d3e --- /dev/null +++ b/lib/private/Authentication/TwoFactorAuth/Manager.php @@ -0,0 +1,141 @@ +<?php + +/** + * @author Christoph Wurst <christoph@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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\Authentication\TwoFactorAuth; + +use OC; +use OC\App\AppManager; +use OCP\AppFramework\QueryException; +use OCP\Authentication\TwoFactorAuth\IProvider; +use OCP\ISession; +use OCP\IUser; + +class Manager { + + const SESSION_UID_KEY = 'two_factor_auth_uid'; + + /** @var AppManager */ + private $appManager; + + /** @var ISession */ + private $session; + + /** + * @param AppManager $appManager + * @param ISession $session + */ + public function __construct(AppManager $appManager, ISession $session) { + $this->appManager = $appManager; + $this->session = $session; + } + + /** + * Determine whether the user must provide a second factor challenge + * + * @param IUser $user + * @return boolean + */ + public function isTwoFactorAuthenticated(IUser $user) { + return count($this->getProviders($user)) > 0; + } + + /** + * Get a 2FA provider by its ID + * + * @param IUser $user + * @param string $challengeProviderId + * @return IProvider|null + */ + public function getProvider(IUser $user, $challengeProviderId) { + $providers = $this->getProviders($user); + return isset($providers[$challengeProviderId]) ? $providers[$challengeProviderId] : null; + } + + /** + * Get the list of 2FA providers for the given user + * + * @param IUser $user + * @return IProvider[] + */ + public function getProviders(IUser $user) { + $allApps = $this->appManager->getEnabledAppsForUser($user); + $providers = []; + + foreach ($allApps as $appId) { + $info = $this->appManager->getAppInfo($appId); + $providerClasses = $info['two-factor-providers']; + foreach ($providerClasses as $class) { + try { + $provider = OC::$server->query($class); + $providers[$provider->getId()] = $provider; + } catch (QueryException $exc) { + // Provider class can not be resolved, ignore it + } + } + } + + return array_filter($providers, function ($provider) use ($user) { + /* @var $provider IProvider */ + return $provider->isTwoFactorAuthEnabledForUser($user); + }); + } + + /** + * Verify the given challenge + * + * @param string $providerId + * @param IUser $user + * @param string $challenge + * @return boolean + */ + public function verifyChallenge($providerId, IUser $user, $challenge) { + $provider = $this->getProvider($user, $providerId); + if (is_null($provider)) { + return false; + } + + $result = $provider->verifyChallenge($user, $challenge); + if ($result) { + $this->session->remove(self::SESSION_UID_KEY); + } + return $result; + } + + /** + * Check if the currently logged in user needs to pass 2FA + * + * @return boolean + */ + public function needsSecondFactor() { + return $this->session->exists(self::SESSION_UID_KEY); + } + + /** + * Prepare the 2FA login (set session value) + * + * @param IUser $user + */ + public function prepareTwoFactorLogin(IUser $user) { + $this->session->set(self::SESSION_UID_KEY, $user->getUID()); + } + +} diff --git a/lib/private/Server.php b/lib/private/Server.php index 0b7b8f9e403..05945cc5c1f 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -275,6 +275,11 @@ class Server extends ServerContainer implements IServerContainer { }); return $userSession; }); + + $this->registerService('\OC\Authentication\TwoFactorAuth\Manager', function (Server $c) { + return new \OC\Authentication\TwoFactorAuth\Manager($c->getAppManager(), $c->getSession()); + }); + $this->registerService('NavigationManager', function ($c) { return new \OC\NavigationManager(); }); @@ -855,6 +860,13 @@ class Server extends ServerContainer implements IServerContainer { } /** + * @return \OC\Authentication\TwoFactorAuth\Manager + */ + public function getTwoFactorAuthManager() { + return $this->query('\OC\Authentication\TwoFactorAuth\Manager'); + } + + /** * @return \OC\NavigationManager */ public function getNavigationManager() { diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index 7104f46fea2..8db4970aaf2 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -332,6 +332,7 @@ class Session implements IUserSession, Emitter { /** * Tries to login the user with HTTP Basic Authentication * + * @todo do not allow basic auth if the user is 2FA enforced * @param IRequest $request * @return boolean if the login was successful */ diff --git a/lib/public/Authentication/TwoFactorAuth/IProvider.php b/lib/public/Authentication/TwoFactorAuth/IProvider.php new file mode 100644 index 00000000000..da046ef4f2f --- /dev/null +++ b/lib/public/Authentication/TwoFactorAuth/IProvider.php @@ -0,0 +1,93 @@ +<?php + +/** + * @author Christoph Wurst <christoph@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 OCP\Authentication\TwoFactorAuth; + +use OCP\IUser; +use OCP\Template; + +/** + * @since 9.1.0 + */ +interface IProvider { + + /** + * Get unique identifier of this 2FA provider + * + * @since 9.1.0 + * + * @return string + */ + public function getId(); + + /** + * Get the display name for selecting the 2FA provider + * + * Example: "Email" + * + * @since 9.1.0 + * + * @return string + */ + public function getDisplayName(); + + /** + * Get the description for selecting the 2FA provider + * + * Example: "Get a token via e-mail" + * + * @since 9.1.0 + * + * @return string + */ + public function getDescription(); + + /** + * Get the template for rending the 2FA provider view + * + * @since 9.1.0 + * + * @param IUser $user + * @return Template + */ + public function getTemplate(IUser $user); + + /** + * Verify the given challenge + * + * @since 9.1.0 + * + * @param IUser $user + * @param string $challenge + */ + public function verifyChallenge(IUser $user, $challenge); + + /** + * Decides whether 2FA is enabled for the given user + * + * @since 9.1.0 + * + * @param IUser $user + * @return boolean + */ + public function isTwoFactorAuthEnabledForUser(IUser $user); +} |