Allow authentication for specific implementations of PKI #223

This commit is contained in:
Flávio Gomes da Silva Lisboa 2018-06-08 13:54:40 -03:00
parent 37b89e4c32
commit c3caa02a6e
No known key found for this signature in database
GPG key ID: E0242D35221D7251
14 changed files with 492 additions and 8 deletions

3
.gitignore vendored
View file

@ -1,3 +1,6 @@
.buildpath
.project
.settings/
3rdparty/vendor/onelogin/php-saml/certs/
3rdparty/vendor/onelogin/php-saml/demo1/
3rdparty/vendor/onelogin/php-saml/demo2/

View file

@ -1,4 +1,5 @@
<?php
use OCA\User_SAML\Strategies\UserBackend\StrategyManager;
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
@ -39,7 +40,10 @@ $samlSettings = new \OCA\User_SAML\SAMLSettings(
$request
);
$userBackend = new \OCA\User_SAML\UserBackend(
$userBackendStrategy = StrategyManager::getStrategy(true);
$userBackend = (is_null($userBackendStrategy) ? '\OCA\User_SAML\UserBackend' : $userBackendStrategy);
$userBackend = new $userBackend(
$config,
$urlGenerator,
\OC::$server->getSession(),

View file

@ -35,6 +35,8 @@ use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
use OCA\User_SAML\Strategies\Authentication\StrategyManager;
use OCA\User_SAML\AppInfo\Application;
class SAMLController extends Controller {
/** @var ISession */
@ -148,6 +150,10 @@ class SAMLController extends Controller {
* @throws \Exception
*/
public function login() {
if (($strategy = StrategyManager::getStrategy()) != null)
{
return $strategy->login($this->config, $this->urlGenerator, $this->logger, $this->userManager, $this->userBackend, $this->session);
}
$type = $this->config->getAppValue($this->appName, 'type');
switch($type) {
case 'saml':
@ -388,5 +394,4 @@ class SAMLController extends Controller {
$directUrl = $this->urlGenerator->linkToRouteAbsolute('core.login.tryLogin', ['direct' => '1']);
return $directUrl;
}
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* @copyright Copyright (c) 2018 Flávio Gomes da Silva Lisboa <flavio.lisboa@fgsl.eti.br>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML\Strategies;
abstract class AbstractStrategyManager {
/**
* @param string $dir
* @param string $onlyClassName
* @return object|NULL
*/
public static function getStrategy($onlyClassName = false)
{
$dir = static::getStrategyDir();
$files = array_diff(scandir($dir), ['..', '.']);
foreach($files as $file){
// Only one strategy is expected, then first matched is returned
if (substr($file,-12) === 'Strategy.php' && substr($file,0,8) !== 'Abstract'){
$namespace = get_called_class();
$tokens = explode('\\', $namespace);
unset($tokens[count($tokens)-1]);
$namespace = implode('\\', $tokens);
$strategy = $namespace . '\\' . substr($file,0,strlen($file)-4);
return ($onlyClassName ? $strategy : new $strategy());
}
}
return null;
}
/**
* @return string
*/
public static function getStrategyDir()
{
throw new \NotFoundException(get_called_class() . ' must implement ' . __METHOD__);
}
}

View file

@ -0,0 +1,178 @@
<?php
/**
* @copyright Copyright (c) 2018 Flávio Gomes da Silva Lisboa <flavio.lisboa@fgsl.eti.br>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML\Strategies\Authentication;
use OCA\User_SAML\Exceptions\NoUserFoundException;
use OCP\AppFramework\Http;
use OCP\IConfig;
use OCP\ILogger;
use OCP\ISession;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\IUserBackend;
/**
* @link http://iti.gov.br/images/repositorio/legislacao/documentos-principais/DOC-ICP-04.01_-_versao_3.2_ATRIBUICAO_DE_OID_NA_ICP-BRASIL.pdf
*/
abstract class AbstractPKIStrategy implements StrategyInterface {
/**
* OID for required attributes
* @var string
*/
protected $OIDRequiredAttribute;
/**
* name of uid attribute
* @var string
*/
protected $certUidAttribute;
/**
* (non-PHPdoc)
*
* @see \OCA\User_SAML\Strategies\Authentication\StrategyInterface::login()
*/
public function login(IConfig $config, IURLGenerator $urlGenerator, ILogger $logger, IUserManager $userManager, IUserBackend $userBackend, ISession $session) {
$ssoUrl = $urlGenerator->getAbsoluteURL ( '/' );
$uidMapping = $config->getAppValue ( 'user_saml', 'general-uid_mapping', 'SSL_CLIENT_CERT');
// receives certificate
$cert = "";
if (is_array ( $_SERVER [$uidMapping] )) {
$cert = $_SERVER [$uidMapping] [0];
} else {
$cert = $_SERVER [$uidMapping];
}
if (empty ( $cert )) {
$logger->error ( 'Cert "' . $cert . '" is not a valid certificate', [
'app' => $this->appName
] );
throw new \InvalidArgumentException ( 'No valid cert given, please check your configuration' . $cert );
}
try {
// extracts attribute from certificate mapping
$PKIData = $this->parsePKIData ( $cert );
$certUid = $PKIData [$this->OIDRequiredAttribute] [$this->certUidAttribute];
// provide user from a search. Certificate attribute must exist in user search filters
$users = $userManager->search ( $certUid );
// recover nextcloud name of user
$listKeys = array_keys ( $users );
$nextcloud_uid = '';
if (isset ( $listKeys [0] )) {
$nextcloud_uid = $listKeys [0];
}
if (!isset ( $nextcloud_uid )) {
throw new NoUserFoundException ('Nextcloud uid not found');
}
// create user session
$_SERVER [$uidMapping] = $nextcloud_uid;
$session->set ( 'user_saml.samlUserData', $_SERVER );
$user = $userManager->get ( $userBackend->getCurrentUserId () );
if (! ($user instanceof IUser)) {
throw new NoUserFoundException();
}
$user->updateLastLoginTimestamp ();
} catch ( NoUserFoundException $e ) {
$ssoUrl = $urlGenerator->linkToRouteAbsolute ( 'user_saml.SAML.notProvisioned' );
}
return new Http\RedirectResponse ( $ssoUrl );
}
/**
* @param string $certificate
* @return boolean|Ambigous <multitype:, string>
*/
abstract protected function parsePKIData($certificate);
/**
* @param string $oid
* @return string
*/
protected function oid2Hex($oid) {
$abBinary = array ();
$parts = explode ( '.', $oid );
$n = 0;
$b = 0;
for($n = 0; $n < count ( $parts ); $n ++) {
if ($n == 0) {
$b = 40 * $parts [$n];
} elseif ($n == 1) {
$b += $parts [$n];
$abBinary [] = $b;
} else {
$abBinary = $this->xBase128 ( $abBinary, $parts [$n], 1 );
}
}
$value = chr ( 0x06 ) . chr ( count ( $abBinary ) );
foreach ( $abBinary as $item ) {
$value .= chr ( $item );
}
return $value;
}
/**
*
* @param array $ab
* @param integer $q
* @param boolean $flag
* @return Ambigous <boolean, unknown>
*/
protected function xBase128(array $ab, $q, $flag) {
$abc = $ab;
if ($q > 127) {
$abc = $this->xBase128 ( $abc, floor ( $q / 128 ), 0 );
}
$q = $q % 128;
if ($flag) {
$abc [] = $q;
} else {
$abc [] = 0x80 | $q;
}
return $abc;
}
/**
* @param string $pemCertificate
* @return string
*/
protected function pem2Der($pemCertificate) {
$aux = explode ( chr ( 0x0A ), $pemCertificate );
$derCertificate = '';
foreach ( $aux as $i ) {
if ($i != '') {
if (substr ( $i, 0, 5 ) !== '-----') {
$derCertificate .= $i;
}
}
}
return base64_decode ( $derCertificate );
}
}

View file

@ -0,0 +1,43 @@
<?php
/**
* @copyright Copyright (c) 2018 Flávio Gomes da Silva Lisboa <flavio.lisboa@fgsl.eti.br>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML\Strategies\Authentication;
use OCP\IConfig;
use OCP\ILogger;
use OCP\ISession;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\IUserBackend;
interface StrategyInterface {
/**
* @param IConfig $config
* @param IURLGenerator $urlGenerator
* @param ILogger $logger
* @param IUserManager $userManager
* @param UserBackend $userBackend
* @param ISession $session
* @return Http\RedirectResponse
* @throws \Exception
*/
public function login(IConfig $config, IURLGenerator $urlGenerator, ILogger $logger, IUserManager $userManager, IUserBackend $userBackend, ISession $session);
}

View file

@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2018 Flávio Gomes da Silva Lisboa <flavio.lisboa@fgsl.eti.br>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML\Strategies\Authentication;
use OCA\User_SAML\Strategies\AbstractStrategyManager;
class StrategyManager extends AbstractStrategyManager {
/**
* @return string
*/
public static function getStrategyDir()
{
return __DIR__;
}
}

View file

@ -0,0 +1,82 @@
<?php
/**
* @copyright Copyright (c) 2018 Flávio Gomes da Silva Lisboa <flavio.lisboa@fgsl.eti.br>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML\Strategies\UserBackend;
interface StrategyInterface {
/**
* delete a user
* @param string $uid The username of the user to delete
* @return bool
* @since 4.5.0
*/
public function deleteUser($uid);
/**
* Get a list of all users
*
* @param string $search
* @param null|int $limit
* @param null|int $offset
* @return string[] an array of all uids
* @since 4.5.0
*/
public function getUsers($search = '', $limit = null, $offset = null);
/**
* check if a user exists
* @param string $uid the username
* @return boolean
* @since 4.5.0
*/
public function userExists($uid);
public function setDisplayName($uid, $displayName);
/**
* Get display name of the user
*
* @param string $uid user ID of the user
* @return string display name
* @since 4.5.0
*/
public function getDisplayName($uid);
/**
* Get a list of all display names and user ids.
*
* @param string $search
* @param string|null $limit
* @param string|null $offset
* @return array an array of all displayNames (value) and the corresponding uids (key)
* @since 4.5.0
*/
public function getDisplayNames($search = '', $limit = null, $offset = null);
/**
* In case the user has been authenticated by Apache true is returned.
*
* @return boolean whether Apache reports a user as currently logged in.
* @since 6.0.0
*/
public function isSessionActive();
}

View file

@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2018 Flávio Gomes da Silva Lisboa <flavio.lisboa@fgsl.eti.br>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML\Strategies\UserBackend;
use OCA\User_SAML\Strategies\AbstractStrategyManager;
class StrategyManager extends AbstractStrategyManager {
/**
* @return string
*/
public static function getStrategyDir()
{
return __DIR__;
}
}

View file

@ -1,7 +1,10 @@
<?php
script('user_saml', 'admin');
style('user_saml', 'admin');
$customTemplate = __DIR__ . DIRECTORY_SEPARATOR . 'custom' . DIRECTORY_SEPARATOR . basename(__FILE__);
if (file_exists($customTemplate)):
include $customTemplate;
else:
script('user_saml', 'admin');
style('user_saml', 'admin');
/** @var array $_ */
?>
<form id="user-saml" class="section" action="#" method="post" data-type="<?php p($_['type']) ?>">
@ -136,3 +139,6 @@ style('user_saml', 'admin');
<span class="success hidden" id="user-saml-settings-complete"><?php p($l->t('Metadata valid')) ?></span>
</div>
</form>
<?php
endif;
?>

View file

@ -0,0 +1,10 @@
Customized templates
====================
Here you can custom content of templates.
It's enough to create a file with the same name of template, and it will be used instead of former file.
For instance, you want to customize notProvisioned.php. Create a notProvisioned.php inside folder custom and fill it with content that you need.

15
templates/error.php Normal file
View file

@ -0,0 +1,15 @@
<?php
$customTemplate = __DIR__ . DIRECTORY_SEPARATOR . 'custom' . DIRECTORY_SEPARATOR . basename(__FILE__);
if (file_exists($customTemplate)):
include $customTemplate;
else:
?>
<ul>
<li class="error">
<?php p($l->t('Authentication Error')) ?><br>
<p class="hint"><?php p($_['message']) ?></p>
</li>
</ul>
<?php
endif;
?>

View file

@ -1,6 +1,15 @@
<?php
$customTemplate = __DIR__ . DIRECTORY_SEPARATOR . 'custom' . DIRECTORY_SEPARATOR . basename(__FILE__);
if (file_exists($customTemplate)):
include $customTemplate;
else:
?>
<ul>
<li class="error">
<?php p($l->t('Account not provisioned.')) ?><br>
<p class="hint"><?php p($l->t('Your account is not provisioned, access to this service is thus not possible.')) ?></p>
</li>
</ul>
<?php
endif;
?>

View file

@ -1,10 +1,13 @@
<?php
style('user_saml', 'selectUserBackEnd');
$customTemplate = __DIR__ . DIRECTORY_SEPARATOR . 'custom' . DIRECTORY_SEPARATOR . basename(__FILE__);
if (file_exists($customTemplate)):
include $customTemplate;
else:
style('user_saml', 'selectUserBackEnd');
/** @var array $_ */
/** @var $l \OCP\IL10N */
?>
<div id="saml-select-user-back-end">
<h1>Choose login option:</h1>
@ -18,3 +21,6 @@ style('user_saml', 'selectUserBackEnd');
</div>
</div>
<?php
endif;
?>