Skip to content

Commit

Permalink
Merge pull request #388 from LDAPAccountManager/feature/372-webauthn-…
Browse files Browse the repository at this point in the history
…names

Feature/372 webauthn names
  • Loading branch information
gruberroland authored Nov 18, 2024
2 parents fd665fe + de2eb59 commit 4ddd8f4
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 18 deletions.
2 changes: 1 addition & 1 deletion lam/HISTORY
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
December 2024 9.0
- Unix users: allow to create group with same name via account profile (#332)
- Group of (unique) names, organisational roles: added member/owner count to PDF fields
- Usability improvements (342, 350)
- Usability improvements (342, 350, 372)
- LAM Pro:
-> Request access: added comment field for owners/approvers (339)
-> Custom scripts: support custom label for module (329)
Expand Down
21 changes: 21 additions & 0 deletions lam/lib/account.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,27 @@ function compareLDAPEntriesByDn(array $a, array $b): int {
return compareDN($a['dn'], $b['dn']);
}

/**
* Does a Base64 encoding that is URL safe.
*
* @param string $data input
* @return string encoded output
*/
function lam_base64url_encode(string $data): string {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

/**
* Does a Base64 decoding that is URL safe.
*
* @param string $data encoded input
* @return string decoded output
*/
function lam_base64url_decode(string $data): string {
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '='), true);
}


/**
* Formats an LDAP time string (e.g. from createTimestamp).
*
Expand Down
2 changes: 1 addition & 1 deletion lam/lib/baseModule.inc
Original file line number Diff line number Diff line change
Expand Up @@ -2160,7 +2160,7 @@ abstract class baseModule {
*
* Calling this method does not require the existence of an enclosing {@link accountContainer}.
*
* @param string $fields input fields
* @param array $fields input fields
* @param array $attributes LDAP attributes
* @param boolean $passwordChangeOnly indicates that the user is only allowed to change his password and no LDAP content is readable
* @param array $readOnlyFields list of read-only fields
Expand Down
27 changes: 27 additions & 0 deletions lam/lib/webauthn.inc
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,33 @@ abstract class PublicKeyCredentialSourceRepositoryBase implements PublicKeyCrede
return $devices;
}

/**
* Performs a full-text search on the user names and returns all devices found.
*
* @param string $userDn user DN
* @return array list of devices array('dn' => ..., 'credentialId' => ..., 'lastUseTime' => ..., 'registrationTime' => ...)
*/
public function getAllUserDevices(string $userDn) {
$pdo = $this->getPDO();
$statement = $pdo->prepare('select * from ' . $this->getTableName() . ' where userDN = :userDn order by userId,registrationTime');
$statement->execute([
':userDn' => $userDn
]);
$results = $statement->fetchAll();
$devices = [];
foreach ($results as $result) {
$name = !empty($result['name']) ? $result['name'] : '';
$devices[] = [
'dn' => $result['userDN'],
'credentialId' => $result['credentialId'],
'lastUseTime' => $result['lastUseTime'],
'registrationTime' => $result['registrationTime'],
'name' => $name
];
}
return $devices;
}

/**
* Deletes a single device from the database.
*
Expand Down
31 changes: 16 additions & 15 deletions lam/templates/lib/500_lam.js
Original file line number Diff line number Diff line change
Expand Up @@ -2152,7 +2152,7 @@ window.lam.webauthn.removeOwnDevice = function(event, isSelfService) {
if (isSelfService) {
action = action + '&selfservice=true&module=webauthn&scope=user';
}
window.lam.webauthn.removeDeviceDialog(element, action, successCallback);
window.lam.webauthn.removeDeviceDialog(element, action, successCallback, isSelfService);
return false;
}

Expand All @@ -2162,8 +2162,9 @@ window.lam.webauthn.removeOwnDevice = function(event, isSelfService) {
* @param element delete button
* @param action action for request (delete|deleteOwn)
* @param successCallback callback if all was fine (optional)
* @param isSelfService run in self service or admin context
*/
window.lam.webauthn.removeDeviceDialog = function(element, action, successCallback) {
window.lam.webauthn.removeDeviceDialog = function(element, action, successCallback, isSelfService) {
const dialogTitle = element.dataset.dialogtitle;
const okText = element.dataset.oktext;
const cancelText = element.dataset.canceltext;
Expand All @@ -2178,7 +2179,7 @@ window.lam.webauthn.removeDeviceDialog = function(element, action, successCallba
width: 'auto'
}).then(result => {
if (result.isConfirmed) {
window.lam.webauthn.sendRemoveDeviceRequest(element, action, successCallback);
window.lam.webauthn.sendRemoveDeviceRequest(element, action, successCallback, isSelfService);
}
});
}
Expand All @@ -2189,8 +2190,9 @@ window.lam.webauthn.removeDeviceDialog = function(element, action, successCallba
* @param element button element
* @param action action (delete|deleteOwn)
* @param successCallback callback if all was fine (optional)
* @param isSelfService run in self service or admin context
*/
window.lam.webauthn.sendRemoveDeviceRequest = function(element, action, successCallback) {
window.lam.webauthn.sendRemoveDeviceRequest = function(element, action, successCallback, isSelfService) {
const dn = element.dataset.dn;
const credential = element.dataset.credential;
const resultDiv = document.getElementById('webauthn_results');
Expand All @@ -2200,6 +2202,11 @@ window.lam.webauthn.sendRemoveDeviceRequest = function(element, action, successC
data.append('action', 'delete');
data.append('dn', dn);
data.append('credentialId', credential);
if (isSelfService) {
document.querySelectorAll('.webauthn_device_name').forEach(item => {
data.append(item.name, item.value);
});
}
fetch('../misc/ajax.php?function=' + action, {
method: 'POST',
body: data
Expand All @@ -2222,9 +2229,8 @@ window.lam.webauthn.sendRemoveDeviceRequest = function(element, action, successC
* Updates a device name.
*
* @param event click event
* @param isSelfService run in self service or admin context
*/
window.lam.webauthn.updateOwnDeviceName = function(event, isSelfService) {
window.lam.webauthn.updateOwnDeviceName = function(event) {
event.preventDefault();
const element = event.currentTarget;
const dn = element.dataset.dn;
Expand All @@ -2241,20 +2247,12 @@ window.lam.webauthn.updateOwnDeviceName = function(event, isSelfService) {
data.append('name', name);
data.append('credentialId', credential);
let action = 'webauthnOwnDevices';
if (isSelfService) {
action = action + '&selfservice=true&module=webauthn&scope=user';
}
fetch('../misc/ajax.php?function=' + action, {
method: 'POST',
body: data
})
.then(async response => {
if (isSelfService) {
nameElement.classList.add('markPass');
}
else {
window.location.href = 'webauthn.php?updated=' + encodeURIComponent(credential);
}
window.location.href = 'webauthn.php?updated=' + encodeURIComponent(credential);
})
.catch(function(err) {
console.log('WebAuthn device name change failed: ' + err.message);
Expand Down Expand Up @@ -2288,6 +2286,9 @@ window.lam.webauthn.registerOwnDevice = function(event, isSelfService) {
data.append('action', 'register');
data.append('dn', dn);
data.append('credential', btoa(JSON.stringify(publicKeyCredential)));
document.querySelectorAll('.webauthn_device_name').forEach(item => {
data.append(item.name, item.value);
});
fetch('../misc/ajax.php?selfservice=true&module=webauthn&scope=user', {
method: 'POST',
body: data
Expand Down
2 changes: 1 addition & 1 deletion lam/templates/tools/webauthn.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
$saveButton->addDataAttribute('credential', $credentialId);
$saveButton->addDataAttribute('dn', $result['dn']);
$saveButton->addDataAttribute('nameelement', 'deviceName_' . $id);
$saveButton->setOnClick('window.lam.webauthn.updateOwnDeviceName(event, false);');
$saveButton->setOnClick('window.lam.webauthn.updateOwnDeviceName(event);');
$nameField = new htmlInputField('deviceName_' . $id, $result['name']);
$nameFieldClasses = ['maxwidth20'];
if (!empty($_GET['updated']) && ($_GET['updated'] === $credentialId)) {
Expand Down

0 comments on commit 4ddd8f4

Please sign in to comment.