From 9e1e0634e6280017ee2ea6ea65aba3510c682f38 Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Sun, 5 Jan 2020 19:05:55 +0100 Subject: [PATCH] webauthn --- lam/graphics/webauthn.png | Bin 0 -> 810 bytes lam/lib/tools/webauthn.inc | 133 +++++++++++++++++++++++++++++++ lam/lib/webauthn.inc | 4 +- lam/templates/lib/500_lam.js | 36 +++++++-- lam/templates/misc/ajax.php | 24 +++++- lam/templates/tools/webauthn.php | 117 +++++++++++++++++++++++++++ lam/tests/lib/webauthnDbTest.php | 5 +- 7 files changed, 306 insertions(+), 13 deletions(-) create mode 100644 lam/graphics/webauthn.png create mode 100644 lam/lib/tools/webauthn.inc create mode 100644 lam/templates/tools/webauthn.php diff --git a/lam/graphics/webauthn.png b/lam/graphics/webauthn.png new file mode 100644 index 0000000000000000000000000000000000000000..31072bb20ff9bf505cb2154d0186f69c6757ab2b GIT binary patch literal 810 zcmV+_1J(SAP)xgoiR(ln7$N*j<2nZ+6G?UQ&B zq~Z!cl6`G7SFXPH?6;2y-o5eRbfIN6mIxuhImmL4y?vaC@d}gU6;?I|^sJ}ySm()w zm!J6&!0ZTyffrp0XQt)|Bp}uyi@)DRc~4}7r9avno~qIt4k?_I9d1`(aeV_Sv*g7W(B zy^QSp(@aTSC!Bp^hKVSFfks81nMRGr8)Hn?5>(*ReFyxKck*~(_XzG+Ns}*Cc zEvd$ajdTcJ5U)`Pp(vb}{>6*eMn$5rLgj}9+}mEu+VcIox0HC>ZfC@y&`z_54=5PX zhDXduoL=}dbgetTwoFactorAuthentication() === TwoFactorProviderService::TWO_FACTOR_WEBAUTHN); + } + + /** + * Returns if a tool may be hidden by configuration in the LAM server profile. + * + * @return boolean hideable + */ + function isHideable() { + return true; + } + +} + +?> \ No newline at end of file diff --git a/lam/lib/webauthn.inc b/lam/lib/webauthn.inc index 9b1fabac..21e4fc6b 100644 --- a/lam/lib/webauthn.inc +++ b/lam/lib/webauthn.inc @@ -49,7 +49,7 @@ use \LAMException; /* This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) - Copyright (C) 2019 Roland Gruber + Copyright (C) 2019 - 2020 Roland Gruber 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 @@ -546,7 +546,7 @@ class PublicKeyCredentialSourceRepositorySQLite implements PublicKeyCredentialSo $pdo = $this->getPDO(); $statement = $pdo->prepare('select * from ' . self::TABLE_NAME . ' where userId like :searchTerm'); $statement->execute(array( - ':searchTerm' => '%' . $searchTerm . '%' + ':searchTerm' => $searchTerm )); $results = $statement->fetchAll(); $devices = array(); diff --git a/lam/templates/lib/500_lam.js b/lam/templates/lib/500_lam.js index 449b56eb..7a2856b1 100644 --- a/lam/templates/lib/500_lam.js +++ b/lam/templates/lib/500_lam.js @@ -1,7 +1,7 @@ /** This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) - Copyright (C) 2003 - 2019 Roland Gruber + Copyright (C) 2003 - 2020 Roland Gruber 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 @@ -1581,18 +1581,41 @@ window.lam.webauthn.addDeviceActionListeners = function() { /** * Removes a webauthn device. * - * @param element button + * @param event click event */ window.lam.webauthn.removeDevice = function(event) { event.preventDefault(); const element = jQuery(event.target); + window.lam.webauthn.removeDeviceDialog(element, 'webauthnDevices'); + return false; +} + +/** + * Removes a user's own webauthn device. + * + * @param event click event + */ +window.lam.webauthn.removeOwnDevice = function(event) { + event.preventDefault(); + const element = jQuery(event.target); + window.lam.webauthn.removeDeviceDialog(element, 'webauthnOwnDevices'); + return false; +} + +/** + * Opens the remove device diaog. + * + * @param element delete button + * @param action action for request (delete|deleteOwn) + */ +window.lam.webauthn.removeDeviceDialog = function(element, action) { const dialogTitle = element.data('dialogtitle'); const okText = element.data('oktext'); const cancelText = element.data('canceltext'); let buttonList = {}; buttonList[okText] = function() { jQuery('#webauthnDeleteConfirm').dialog('close'); - window.lam.webauthn.sendRemoveDeviceRequest(element); + window.lam.webauthn.sendRemoveDeviceRequest(element, action); }; buttonList[cancelText] = function() { jQuery(this).dialog("close"); @@ -1604,16 +1627,15 @@ window.lam.webauthn.removeDevice = function(event) { buttons: buttonList, width: 'auto' }); - - return false; } /** * Sends the remove request to server. * * @param element button element + * @param action action (delete|deleteOwn) */ -window.lam.webauthn.sendRemoveDeviceRequest = function(element) { +window.lam.webauthn.sendRemoveDeviceRequest = function(element, action) { const dn = element.data('dn'); const credential = element.data('credential'); const resultDiv = jQuery('#webauthn_results'); @@ -1626,7 +1648,7 @@ window.lam.webauthn.sendRemoveDeviceRequest = function(element) { credentialId: credential }; jQuery.ajax({ - url: '../misc/ajax.php?function=webauthnDevices', + url: '../misc/ajax.php?function=' + action, method: 'POST', data: data }) diff --git a/lam/templates/misc/ajax.php b/lam/templates/misc/ajax.php index f709a544..3cda46f1 100644 --- a/lam/templates/misc/ajax.php +++ b/lam/templates/misc/ajax.php @@ -15,7 +15,7 @@ use \LAMCfgMain; /* This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) - Copyright (C) 2011 - 2019 Roland Gruber + Copyright (C) 2011 - 2020 Roland Gruber 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 @@ -159,6 +159,9 @@ class Ajax { ob_end_clean(); echo $jsonOut; } + elseif ($function === 'webauthnOwnDevices') { + $this->manageWebauthnOwnDevices(); + } } /** @@ -255,7 +258,7 @@ class Ajax { private function manageWebauthnDevicesSearch($searchTerm) { include_once __DIR__ . '/../../lib/webauthn.inc'; $database = new \LAM\LOGIN\WEBAUTHN\PublicKeyCredentialSourceRepositorySQLite(); - $results = $database->searchDevices($searchTerm); + $results = $database->searchDevices('%' . $searchTerm . '%'); $row = new htmlResponsiveRow(); $row->addVerticalSpacer('0.5rem'); if (empty($results)) { @@ -326,6 +329,23 @@ class Ajax { echo json_encode(array('content' => $content)); } + /** + * Manages requests to setup user's own webauthn devices. + */ + private function manageWebauthnOwnDevices() { + $action = $_POST['action']; + $dn = $_POST['dn']; + $sessionDn = $_SESSION['ldap']->getUserName(); + if ($sessionDn !== $dn) { + logNewMessage(LOG_ERR, 'Webauthn delete canceled, DN does not match.'); + die(); + } + if ($action === 'delete') { + $credentialId = $_POST['credentialId']; + $this->manageWebauthnDevicesDelete($dn, $credentialId); + } + } + /** * Handles DN selection fields. * diff --git a/lam/templates/tools/webauthn.php b/lam/templates/tools/webauthn.php new file mode 100644 index 00000000..3b81b592 --- /dev/null +++ b/lam/templates/tools/webauthn.php @@ -0,0 +1,117 @@ +'; +echo "
\n"; +$tabindex = 1; +$container = new htmlResponsiveRow(); + +$container->add(new htmlTitle(_("Webauthn devices")), 12); + +$userDn = $_SESSION['ldap']->getUserName(); +$database = new PublicKeyCredentialSourceRepositorySQLite(); +$results = $database->searchDevices($userDn); +$container->addVerticalSpacer('0.5rem'); +$buttonGroup = new htmlGroup(); +$reloadButton = new htmlButton('reload', _('Reload')); +$reloadButton->setIconClass('refreshButton'); +$buttonGroup->addElement($reloadButton); +$container->add($buttonGroup, 12); +$container->addVerticalSpacer('2rem'); +if (empty($results)) { + $container->add(new htmlStatusMessage('INFO', _('No devices found.')), 12); +} +else { + $titles = array( + _('Registration'), + _('Last use'), + _('Delete') + ); + $data = array(); + $id = 0; + foreach ($results as $result) { + $delButton = new htmlButton('deleteDevice' . $id, 'delete.png', true); + $delButton->addDataAttribute('credential', $result['credentialId']); + $delButton->addDataAttribute('dn', $result['dn']); + $delButton->addDataAttribute('dialogtitle', _('Remove device')); + $delButton->addDataAttribute('oktext', _('Ok')); + $delButton->addDataAttribute('canceltext', _('Cancel')); + $delButton->setOnClick('window.lam.webauthn.removeOwnDevice(event);'); + $data[] = array( + new htmlOutputText(date('Y-m-d H:i:s', $result['registrationTime'])), + new htmlOutputText(date('Y-m-d H:i:s', $result['lastUseTime'])), + $delButton + ); + $id++; + } + $table = new htmlResponsiveTable($titles, $data); + $tableDiv = new htmlDiv('webauthn_results', $table); + $tableDiv->addDataAttribute('sec_token_value', getSecurityTokenValue()); + $container->add($tableDiv, 12); +} +$container->addVerticalSpacer('2rem'); + +$confirmationDiv = new htmlDiv('webauthnDeleteConfirm', new htmlOutputText(_('Do you really want to remove this device?')), array('hidden')); +$container->add($confirmationDiv, 12); + + +parseHtml(null, $container, array(), false, $tabindex, 'user'); + +echo '
'; +echo ''; +include __DIR__ . '/../../lib/adminFooter.inc'; + +?> diff --git a/lam/tests/lib/webauthnDbTest.php b/lam/tests/lib/webauthnDbTest.php index d6478b19..1d597564 100644 --- a/lam/tests/lib/webauthnDbTest.php +++ b/lam/tests/lib/webauthnDbTest.php @@ -148,8 +148,9 @@ class PublicKeyCredentialSourceRepositorySQLiteTest extends TestCase { "uh1", 1); $this->database->saveCredentialSource($source1); - $this->assertNotEmpty($this->database->searchDevices('h1')); - $this->assertEmpty($this->database->searchDevices('h2')); + $this->assertNotEmpty($this->database->searchDevices('uh1')); + $this->assertNotEmpty($this->database->searchDevices('%h1%')); + $this->assertEmpty($this->database->searchDevices('uh2')); } public function test_deleteDevice() {