Verified Commit 717ca9a5 authored by Côme Chilliet's avatar Côme Chilliet
Browse files

feat(totp) Add TOTP user tab to store TOTP registrations

Depends upon otphp and phpqrcode

issue #6020
parent 1ceea0ff
##
## totp-fd.schema - Needed by Fusion Directory for managing TOTP registrations
##
# Attributes
attributetype ( 1.3.6.1.4.1.38414.75.1.1 NAME 'fdTOTPTokens'
DESC 'FusionDirectory - TOTP registrations stored as date|uri'
EQUALITY caseExactIA5Match
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26)
# Objectclasses
objectclass (1.3.6.1.4.1.38414.75.2.1 NAME 'fdTOTPUser' SUP top AUXILIARY
DESC 'FusionDirectory - User TOTP tab'
MUST ( fdTOTPTokens ) )
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2018-2019 FusionDirectory
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* Require PHPQRCODE */
require_once('/usr/share/phpqrcode/qrlib.php');
$data = urldecode($_GET['data']);
QRcode::png($data);
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2003 Cajus Pollmeier
Copyright (C) 2011-2019 FusionDirectory
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* Require TOTP and dependencies */
require_once('otphp/OTPInterface.php');
require_once('otphp/TOTPInterface.php');
require_once('otphp/ParameterTrait.php');
require_once('otphp/OTP.php');
require_once('otphp/TOTP.php');
require_once('beberlei/assert/lib/Assert/AssertionFailedException.php');
require_once('beberlei/assert/lib/Assert/InvalidArgumentException.php');
require_once('beberlei/assert/lib/Assert/Assertion.php');
require_once('paragonie/constant_time_encoding/src/Binary.php');
require_once('paragonie/constant_time_encoding/src/EncoderInterface.php');
require_once('paragonie/constant_time_encoding/src/Base32.php');
class TOTPAddDialog extends GenericDialog
{
protected $otp;
protected $otpcode;
function __construct ($simplePlugin, $attribute)
{
$this->attribute = $attribute;
if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$host = $_SERVER['HTTP_X_FORWARDED_HOST'];
} else {
$host = $_SERVER['SERVER_NAME'];
}
/* Create a TOTP object */
$this->otp = OTPHP\TOTP::create();
$this->otp->setLabel($this->attribute->getUserUid());
$this->otp->setIssuer($host);
}
function save_object ()
{
$this->otpcode = ($_POST['otpcode'] ?? '');
}
function check(): array
{
/* Check that code was filled and correct */
if (empty($this->otpcode)) {
return [_('OTP code missing')];
} elseif (!$this->otp->verify($this->otpcode)) {
return [_('Wrong OTP code')];
}
return [];
}
function dialog_execute ()
{
global $config;
$this->save_object();
$provisioningUri = $this->otp->getProvisioningUri();
/* Render */
$smarty = get_smarty();
$smarty->assign('usePrototype', 'true');
$smarty->assign('section', _('Add TOTP device'));
$smarty->assign('sectionId', 'totp');
$smarty->assign('sectionClasses', '');
$attributes = [];
$attributes['otpuri'] = [
'htmlid' => 'otpuri',
'label' => '{literal}'._('QR Code').'{/literal}',
'description' => _('Scan this QR code and enter an OTP code below'),
'input' => '<img src="qrcode.php?data='.urlencode($provisioningUri).'" alt="'.$provisioningUri.'"/>',
'subattribute' => FALSE,
'required' => FALSE,
'readable' => TRUE,
'writable' => FALSE,
];
$attributes['otpcode'] = [
'htmlid' => 'otpcode',
'label' => '{literal}'._('Code').'{/literal}',
'description' => _('After scanning the QR code, enter here the digits generated by you OTP device or application'),
'input' => '<input type="text" name="otpcode" id="otpcode" required="required"/>',
'subattribute' => FALSE,
'required' => TRUE,
'readable' => TRUE,
'writable' => TRUE,
];
$smarty->assign('attributes', $attributes);
$sections = [
'totp' => $smarty->fetch(get_template_path('simpleplugin_section.tpl'))
];
$smarty->assign('sections', $sections);
$smarty->assign('hiddenPostedInput', get_class($this).'_posted');
return $smarty->fetch(get_template_path('simpleplugin.tpl'))."\n".$this->buttons();
}
function buttons ()
{
return '<div style="width:100%; text-align:right; clear:both; float:none;">'.
' <input type="submit" name="'.$this->post_finish.'" value="'.msgPool::addButton().'"/>&nbsp;'.
' <input type="submit" formnovalidate="formnovalidate" name="'.$this->post_cancel.'" value="'.msgPool::cancelButton().'"/>'.
'</div>';
}
function handle_finish ()
{
$this->save_object();
$msgs = $this->check();
if (count($msgs)) {
msg_dialog::displayChecks($msgs);
return $this->dialog_execute();
}
$this->attribute->addValue('', [date('Y-m-d'), $this->otp->getProvisioningUri()]);
return FALSE;
}
function handle_cancel ()
{
return FALSE;
}
}
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2018-2019 FusionDirectory
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
class TOTPRegistrationsAttribute extends DialogOrderedArrayAttribute
{
protected $order = FALSE;
protected $dialogClass = 'TOTPAddDialog';
function __construct ($label, $description, $ldapName, $required = FALSE, $defaultValue = [], $acl = "")
{
parent::__construct($label, $description, $ldapName, $required, $defaultValue, $acl);
$this->edit_enabled = FALSE;
}
/*! \brief Convert text value to displayable array value */
protected function getAttributeArrayValue ($key, $value)
{
return $value;
}
function readValue ($value)
{
return explode('|', $value, 2);
}
function writeValue ($key, $value)
{
return implode('|', $value);
}
public function getUserUid(): string
{
return $this->plugin->parent->getBaseObject()->uid;
}
}
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2018-2019 FusionDirectory
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
require_once('WebAuthn/WebAuthn.php');
class totpAccount extends simplePlugin
{
var $displayHeader = TRUE;
static function plInfo (): array
{
return [
'plShortName' => _('TOTP'),
'plDescription' => _('Manage double factor authentication'),
'plSelfModify' => TRUE,
'plObjectClass' => ['fdTOTPUser'],
'plObjectType' => ['user'],
'plIcon' => 'geticon.php?context=applications&icon=totp&size=48',
'plSmallIcon' => 'geticon.php?context=applications&icon=totp&size=16',
'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo())
];
}
static function getAttributesInfo (): array
{
return [
'main' => [
'name' => _('TOTP'),
'class' => ['fullwidth'],
'attrs' => [
new TOTPRegistrationsAttribute(
_('Codes'), _('TOTP codes for this user'),
'fdTOTPTokens'
)
]
]
];
}
static public function initTOTPObject ()
{
global $config, $ui;
if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$host = $_SERVER['HTTP_X_FORWARDED_HOST'];
} else {
$host = $_SERVER['SERVER_NAME'];
}
$otp = TOTP::create();
$otp->setLabel($ui->uid); //FIXME
$otp->setIssuer('FusionDirectory');
return $otp;
}
static public function serializeRegistration (object $data): string
{
$safeData = [];
foreach (['rpId','credentialPublicKey','certificateChain','certificate','certificateIssuer','certificateSubject','signatureCounter'] as $stringField) {
$safeData[$stringField] = $data->$stringField;
}
foreach (['credentialId','AAGUID'] as $binField) {
// base64 encode binary fields
$safeData[$binField] = base64_encode($data->$binField);
}
return json_encode($safeData);
}
static public function unserializeRegistration (string $json): array
{
$data = json_decode($json, TRUE);
foreach (['credentialId','AAGUID'] as $binField) {
$data[$binField] = base64_decode($data[$binField]);
}
return $data;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment