Skip to content

Commit

Permalink
Add two factor auth to core and add a dummy provider app for demonstr…
Browse files Browse the repository at this point in the history
…ation
  • Loading branch information
ChristophWurst committed May 17, 2016
1 parent 7dbdf2f commit 6f85fe9
Show file tree
Hide file tree
Showing 29 changed files with 1,368 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
!/apps/user_ldap
!/apps/provisioning_api
!/apps/systemtags
!/apps/twofactor_email
!/apps/updatenotification
/apps/files_external/3rdparty/irodsphp/PHPUnitTest
/apps/files_external/3rdparty/irodsphp/web
Expand Down
1 change: 1 addition & 0 deletions apps/dav/lib/Connector/Sabre/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ protected function validateUserPass($username, $password) {
return true;
} else {
\OC_Util::setUpFS(); //login hooks may need early access to the filesystem
// TODO: do not allow basic auth if the user is 2FA enforced
if($this->userSession->login($username, $password)) {
$this->userSession->createSessionToken($this->request, $username, $password);
\OC_Util::setUpFS($this->userSession->getUser()->getUID());
Expand Down
23 changes: 23 additions & 0 deletions apps/twofactor_email/appinfo/info.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<info>
<id>twofactor_email</id>
<name>Two Factor Email Provider</name>
<description>An Two-Factor-Auth Provider for ownCloud 9.1+</description>
<licence>AGPL</licence>
<author>Christoph Wurst</author>
<version>0.0.1</version>
<namespace>TwoFactor_Email</namespace>
<category>other</category>
<types>
<prelogin/>
<authentication/>
</types>

<two-factor-providers>
<provider>OCA\TwoFactor_Email\Provider\TwoFactorEmailProvider</provider>
</two-factor-providers>

<dependencies>
<owncloud min-version="9.1" max-version="9.1" />
</dependencies>
</info>
5 changes: 5 additions & 0 deletions apps/twofactor_email/js/challenge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(function() {
'use strict';

console.log('if you can see this, 2FA providers can add JS files');
})();
94 changes: 94 additions & 0 deletions apps/twofactor_email/lib/Provider/TwoFactorEmailProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

/**
* @author Christoph Wurst <[email protected]>
*
* @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 OCA\TwoFactor_Email\Provider;

use OCP\Authentication\TwoFactorAuth\IProvider;
use OCP\IUser;
use OCP\Template;

class TwoFactorEmailProvider implements IProvider {

/**
* Get unique identifier of this 2FA provider
*
* @since 9.1.0
*
* @return string
*/
public function getId() {
return 'email';
}

/**
* Get the display name for selecting the 2FA provider
*
* @since 9.1.0
*
* @return string
*/
public function getDisplayName() {
// TODO: L10N
return 'Email';
}

/**
* Get the template for rending the 2FA provider view
*
* @since 9.1.0
*
* @param IUser $user
* @return Template
*/
public function getTemplate(IUser $user) {
return new Template('twofactor_email', 'challenge');
}

/**
* Verify the given challenge
*
* @since 9.1.0
*
* @param IUser $user
* @param string $challenge
*/
public function verifyChallenge(IUser $user, $challenge) {
if ($challenge === 'passme') {
return true;
}
return false;
}

/**
* Decides whether 2FA is enforced for the given user
*
* @since 9.1.0
*
* @param IUser $user
* @return boolean
*/
public function isTwoFactorAuthEnforcedForUser(IUser $user) {
// 2FA is enforced for all users
return true;
}

}
11 changes: 11 additions & 0 deletions apps/twofactor_email/templates/challenge.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

// Script is added for demonstration purposes only
script('twofactor_email', 'challenge');

?>

<form method="POST">
<input type="text" name="challenge">
<input type="submit" class="button" value="Verify">
</form>
13 changes: 13 additions & 0 deletions apps/twofactor_email/tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
/**
* ownCloud - twofactoremail
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Christoph Wurst <[email protected]>
* @copyright Christoph Wurst 2016
*/

require_once __DIR__ . '/../../../tests/bootstrap.php';
require_once __DIR__ . '/../appinfo/autoload.php';
16 changes: 15 additions & 1 deletion core/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use OC\Core\Controller\LoginController;
use OC\Core\Controller\LostController;
use OC\Core\Controller\TokenController;
use OC\Core\Controller\TwoFactorChallengeController;
use OC\Core\Controller\UserController;
use OC_Defaults;
use OCP\AppFramework\App;
Expand Down Expand Up @@ -101,9 +102,19 @@ public function __construct(array $urlParams=array()){
$c->query('Config'),
$c->query('Session'),
$c->query('UserSession'),
$c->query('URLGenerator')
$c->query('URLGenerator'),
$c->query('TwoFactorAuthManager')
);
});
$container->registerService('TwoFactorChallengeController', function (SimpleContainer $c) {
return new TwoFactorChallengeController(
$c->query('AppName'),
$c->query('Request'),
$c->query('TwoFactorAuthManager'),
$c->query('UserSession'),
$c->query('Session'),
$c->query('URLGenerator'));
});
$container->registerService('TokenController', function(SimpleContainer $c) {
return new TokenController(
$c->query('AppName'),
Expand Down Expand Up @@ -168,6 +179,9 @@ public function __construct(array $urlParams=array()){
$container->registerService('DefaultEmailAddress', function() {
return Util::getDefaultEmailAddress('lostpassword-noreply');
});
$container->registerService('TwoFactorAuthManager', function(SimpleContainer $c) {
return $c->query('ServerContainer')->getTwoFactorAuthManager();
});
}

}
16 changes: 14 additions & 2 deletions core/Controller/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

namespace OC\Core\Controller;

use OC;
use OC\Authentication\TwoFactorAuth\Manager;
use OC\User\Session;
use OC_App;
use OC_Util;
Expand Down Expand Up @@ -54,6 +54,9 @@ class LoginController extends Controller {
/** @var IURLGenerator */
private $urlGenerator;

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

/**
* @param string $appName
* @param IRequest $request
Expand All @@ -62,15 +65,17 @@ class LoginController extends Controller {
* @param ISession $session
* @param Session $userSession
* @param IURLGenerator $urlGenerator
* @param Manager $twoFactorManager
*/
function __construct($appName, IRequest $request, IUserManager $userManager, IConfig $config, ISession $session,
Session $userSession, IURLGenerator $urlGenerator) {
Session $userSession, IURLGenerator $urlGenerator, Manager $twoFactorManager) {
parent::__construct($appName, $request);
$this->userManager = $userManager;
$this->config = $config;
$this->session = $session;
$this->userSession = $userSession;
$this->urlGenerator = $urlGenerator;
$this->twoFactorManager = $twoFactorManager;
}

/**
Expand Down Expand Up @@ -167,6 +172,7 @@ public function showLoginForm($user, $redirect_url, $remember_login) {
*/
public function tryLogin($user, $password, $redirect_url) {
// TODO: Add all the insane error handling
/* @var $loginResult IUser */
$loginResult = $this->userManager->checkPassword($user, $password);
if ($loginResult === false) {
$users = $this->userManager->getByEmail($user);
Expand All @@ -185,6 +191,12 @@ public function tryLogin($user, $password, $redirect_url) {
return new RedirectResponse($this->urlGenerator->linkToRoute('core.login.showLoginForm', $args));
}
$this->userSession->createSessionToken($this->request, $loginResult->getUID(), $password);

if ($this->twoFactorManager->isTwoFactorAuthenticated($loginResult)) {
$this->twoFactorManager->prepareTwoFactorLogin($loginResult);
return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge'));
}

if (!is_null($redirect_url) && $this->userSession->isLoggedIn()) {
$location = $this->urlGenerator->getAbsoluteURL(urldecode($redirect_url));
// Deny the redirect if the URL contains a @
Expand Down
134 changes: 134 additions & 0 deletions core/Controller/TwoFactorChallengeController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php

/**
* @author Christoph Wurst <[email protected]>
*
* @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\Core\Controller;

use OC\Authentication\TwoFactorAuth\Manager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IRequest;
use OCP\ISession;
use OCP\IURLGenerator;
use OCP\IUserSession;

class TwoFactorChallengeController extends Controller {

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

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

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

/** @var IURLGenerator */
private $urlGenerator;

/**
* @param string $appName
* @param IRequest $request
* @param Manager $twoFactorManager
* @param IUserSession $userSession
* @param ISession $session
* @param IURLGenerator $urlGenerator
*/
public function __construct($appName, IRequest $request, Manager $twoFactorManager, IUserSession $userSession,
ISession $session, IURLGenerator $urlGenerator) {
parent::__construct($appName, $request);
$this->twoFactorManager = $twoFactorManager;
$this->userSession = $userSession;
$this->session = $session;
$this->urlGenerator = $urlGenerator;
}

/**
* @NoCSRFRequired
* @PublicPage
*
* @return TemplateResponse
*/
public function selectChallenge() {
$user = $this->userSession->getUser();
$providers = $this->twoFactorManager->getProviders($user);

$data = [
'providers' => $providers,
];
return new TemplateResponse($this->appName, 'twofactorselectchallenge', $data, 'guest');
}

/**
* @NoCSRFRequired
* @PublicPage
* @UseSession
*
* @param string $challengeProviderId
* @return TemplateResponse
*/
public function showChallenge($challengeProviderId) {
$user = $this->userSession->getUser();
$provider = $this->twoFactorManager->getProvider($user, $challengeProviderId);
if (is_null($provider)) {
return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge'));
}

if ($this->session->exists('two_factor_auth_error')) {
$this->session->remove('two_factor_auth_error');
$error = true;
} else {
$error = false;
}
$data = [
'error' => $error,
'provider' => $provider,
'template' => $provider->getTemplate($user)->fetchPage(),
];
return new TemplateResponse($this->appName, 'twofactorshowchallenge', $data, 'guest');
}

/**
* @NoCSRFRequired
* @PublicPage
* @UseSession
*
* @param int $challengeProviderId
* @param string $challenge
* @return RedirectResponse
*/
public function solveChallenge($challengeProviderId, $challenge) {
$user = $this->userSession->getUser();
$provider = $this->twoFactorManager->getProvider($user, $challengeProviderId);
if (is_null($provider)) {
return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.selectChallenge'));
}

if ($this->twoFactorManager->verifyChallenge($challengeProviderId, $user, $challenge)) {
return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index'));
}

$this->session->set('two_factor_auth_error', true);
return new RedirectResponse($this->urlGenerator->linkToRoute('core.TwoFactorChallenge.showChallenge', ['challengeProviderId' => $provider->getId()]));
}

}
Loading

0 comments on commit 6f85fe9

Please sign in to comment.