mirror of
https://github.com/LDAPAccountManager/lam.git
synced 2025-10-03 09:49:16 +02:00
Merge remote-tracking branch 'origin/develop' into 413-update-dependencies
This commit is contained in:
commit
45baad01cf
12 changed files with 3896 additions and 3330 deletions
|
@ -14,7 +14,8 @@
|
|||
"ext-xmlreader": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-imagick": "*"
|
||||
"ext-imagick": "*",
|
||||
"ext-gettext": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vendor/bin/phpunit"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
ldap-account-manager (9.1.RC1-1) unstable; urgency=medium
|
||||
ldap-account-manager (9.1-1) unstable; urgency=medium
|
||||
|
||||
* new upstream release
|
||||
|
||||
-- Roland Gruber <post@rolandgruber.de> Tue, 25 Feb 2024 07:36:27 +0200
|
||||
-- Roland Gruber <post@rolandgruber.de> Thu, 13 Mar 2025 07:36:27 +0200
|
||||
|
||||
ldap-account-manager (9.0-1) unstable; urgency=medium
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
FROM debian:bookworm-slim
|
||||
LABEL maintainer="Roland Gruber <post@rolandgruber.de>"
|
||||
|
||||
ARG LAM_RELEASE=9.1.RC1
|
||||
ARG LAM_RELEASE=9.1
|
||||
EXPOSE 80
|
||||
|
||||
ENV \
|
||||
|
|
|
@ -3,7 +3,7 @@ services:
|
|||
ldap-account-manager:
|
||||
build:
|
||||
context: .
|
||||
image: ldapaccountmanager/lam:9.1.RC1
|
||||
image: ldapaccountmanager/lam:9.1
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:80"
|
||||
|
|
12
lam/HISTORY
12
lam/HISTORY
|
@ -1,16 +1,20 @@
|
|||
March 2025 9.1
|
||||
June 2025 9.2
|
||||
- Active Directory: allow to restore deleted entries in tree view (415)
|
||||
|
||||
|
||||
13.03.2025 9.1
|
||||
- Usability improvements (347, 348, 360, 403)
|
||||
- Active Directory: deleted entries in "CN=Deleted Objects" can be shown (option in server profile, advanced settings)
|
||||
- Security: LAM no longer ships with any default passwords, main configuration password is requested on login if not yet set (390)
|
||||
- Docker: support to read e.g. configuration password from file to support Docker swarm
|
||||
- LAM Pro:
|
||||
-> Added support to manage DNS entries of bind-dyndb-ldap (361)
|
||||
-> Unix users: support to create a group with same name for rfc2307bis (404)
|
||||
- Fixed bugs:
|
||||
-> Ambiguous tooltip on profile editor for Shadow users (394)
|
||||
-> Self service photo file enhancements (396)
|
||||
-> Tree view: delete does not work in French (406)
|
||||
-> Cron job mails: show all values for multi-value attribute wildcards (411)
|
||||
- LAM Pro:
|
||||
-> Added support to manage DNS entries of bind-dyndb-ldap (361)
|
||||
-> Unix users: support to create a group with same name for rfc2307bis (404)
|
||||
|
||||
|
||||
17.12.2024 9.0
|
||||
|
|
|
@ -1 +1 @@
|
|||
9.1.RC1
|
||||
9.1
|
||||
|
|
|
@ -543,10 +543,7 @@
|
|||
|
||||
<para>Show deleted entries: This is for Active Directory and Samba 4
|
||||
only. It will unhide LDAP entries in "CN=Deleted Objects,DC=...". You
|
||||
can use this to browse these entries in tree view. To restore an entry
|
||||
run "Restore-ADObject -Identity GUID" in PowerShell where GUID is the
|
||||
value of the "objectGUID" attribute (you might need to base64 decode
|
||||
it).</para>
|
||||
can use this to browse and restore these entries in tree view.</para>
|
||||
|
||||
<para>Referential integrity overlay: Activate this checkbox if you
|
||||
have any server side extension for referential integrity in place. In
|
||||
|
|
|
@ -70,6 +70,7 @@ include_once __DIR__ . '/tools/treeview.inc';
|
|||
* @package LAM\TOOLS\TREEVIEW
|
||||
*/
|
||||
class TreeView {
|
||||
const AD_DELETED_DELIMITER = '\0ADEL:';
|
||||
|
||||
/**
|
||||
* @var array schema attributes
|
||||
|
@ -124,6 +125,10 @@ class TreeView {
|
|||
$this->ensureWriteAccess();
|
||||
$this->validateDn($dn);
|
||||
return $this->deleteNode($dn);
|
||||
case 'restoreNode':
|
||||
$this->ensureWriteAccess();
|
||||
$this->validateDn($dn);
|
||||
return $this->restoreNode($dn);
|
||||
case 'search':
|
||||
$this->validateDn($dn);
|
||||
return $this->search($dn);
|
||||
|
@ -366,10 +371,10 @@ class TreeView {
|
|||
$row->addVerticalSpacer('1rem');
|
||||
}
|
||||
$row->add(new htmlTitle(unescapeLdapSpecialCharacters($dn)));
|
||||
$this->addActionBar($row, $dn);
|
||||
$attributes = ldapGetDN($dn, ['*']);
|
||||
$this->addActionBar($row, $dn, $attributes);
|
||||
$row->add(new htmlDiv('ldap_actionarea_messages', new htmlOutputText('')));
|
||||
$row->add(new htmlSubTitle(_('Attributes')));
|
||||
$attributes = ldapGetDN($dn, ['*']);
|
||||
unset($attributes['dn']);
|
||||
ksort($attributes);
|
||||
logNewMessage(LOG_DEBUG, 'LDAP attributes for ' . $dn . ': ' . print_r($attributes, true));
|
||||
|
@ -471,11 +476,13 @@ class TreeView {
|
|||
*
|
||||
* @param htmlResponsiveRow $row container
|
||||
* @param string $dn entry DN
|
||||
* @param array $attributes LDAP attributes
|
||||
*/
|
||||
private function addActionBar(htmlResponsiveRow $row, string $dn): void {
|
||||
private function addActionBar(htmlResponsiveRow $row, string $dn, array $attributes): void {
|
||||
$buttonGroup = new htmlGroup();
|
||||
$buttonSize = '16px';
|
||||
$buttonClasses = ['clickable', 'lam-margin-small', 'icon'];
|
||||
$isRestorable = isset($attributes['isdeleted'][0]) && ($attributes['isdeleted'][0] === 'TRUE') && str_contains($dn, '\0ADEL');
|
||||
|
||||
if (checkIfWriteAccessIsAllowed()) {
|
||||
$createButton = new htmlImage('../../graphics/add.svg', $buttonSize, $buttonSize, _('Create a child entry'), _('Create a child entry'));
|
||||
|
@ -484,6 +491,7 @@ class TreeView {
|
|||
$createButton->setCSSClasses($buttonClasses);
|
||||
$buttonGroup->addElement($createButton);
|
||||
|
||||
if (!$isRestorable) {
|
||||
$deleteButton = new htmlImage('../../graphics/delete.svg', $buttonSize, $buttonSize, _('Delete'), _('Delete'));
|
||||
$deleteButton->setOnClick('window.lam.treeview.deleteNode(\'' . getSecurityTokenName() . '\', '
|
||||
. '\'' . getSecurityTokenValue() . '\', \'' . base64_encode($dn) . '\', \'' . base64_encode(htmlspecialchars(extractRDN($dn))) . '\', '
|
||||
|
@ -492,6 +500,7 @@ class TreeView {
|
|||
$deleteButton->setCSSClasses($buttonClasses);
|
||||
$buttonGroup->addElement($deleteButton);
|
||||
}
|
||||
}
|
||||
|
||||
$refreshButton = new htmlImage('../../graphics/refresh.svg', $buttonSize, $buttonSize, _('Refresh'), _('Refresh'));
|
||||
$refreshButton->setOnClick('mar10.Wunderbaum.getTree(\'#ldap_tree\').findKey(\'' . base64_encode($dn) . '\').loadLazy(true); '
|
||||
|
@ -534,7 +543,29 @@ class TreeView {
|
|||
$exportButton->setCSSClasses($buttonClasses);
|
||||
$buttonGroup->addElement($exportButton);
|
||||
}
|
||||
|
||||
$row->add($buttonGroup);
|
||||
|
||||
if (checkIfWriteAccessIsAllowed() && $isRestorable) {
|
||||
$row->addVerticalSpacer('1rem');
|
||||
$row->add(new htmlSubTitle(_('Restore')));
|
||||
$targetDn = extractDNSuffix(extractDNSuffix($dn));
|
||||
if (!empty($attributes['lastknownparent'][0]) && !str_contains($attributes['lastknownparent'][0], self::AD_DELETED_DELIMITER)) {
|
||||
$targetDn = $attributes['lastknownparent'][0];
|
||||
}
|
||||
$restoreTargetDn = new htmlResponsiveInputField(_('Target DN'), 'treeview-restore-dn', $targetDn);
|
||||
$restoreTargetDn->showDnSelection();
|
||||
$row->add($restoreTargetDn);
|
||||
$row->addVerticalSpacer('0.5rem');
|
||||
$restoreButton = new htmlButton('restorebutton', _('Restore'));
|
||||
$rdn = explode(self::AD_DELETED_DELIMITER, $dn)[0];
|
||||
$restoreButton->setOnClick('window.lam.treeview.restoreNode(\'' . getSecurityTokenName() . '\', '
|
||||
. '\'' . getSecurityTokenValue() . '\', \'' . base64_encode($dn) . '\', \'' . base64_encode(htmlspecialchars($rdn)) . '\', '
|
||||
. '\'' . addslashes(_('Restore')) . '\', \'' . addslashes(_('Cancel')) . '\', \'' . addslashes(_('Restore this entry')) . '\', \'' . addslashes(_('Ok')) . '\', '
|
||||
. '\'' . addslashes(_('Error')) . '\');');
|
||||
$restoreButton->setCSSClasses(['lam-secondary']);
|
||||
$row->add($restoreButton, 12, 12, 12, 'text-center');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1359,6 +1390,38 @@ class TreeView {
|
|||
return json_encode([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a node in LDAP.
|
||||
*
|
||||
* @param string $dn DN
|
||||
* @return string JSON
|
||||
*/
|
||||
private function restoreNode(string $dn): string {
|
||||
$rdn = explode(self::AD_DELETED_DELIMITER, $dn)[0];
|
||||
$targetDn = $_POST['targetDn'];
|
||||
$changes = [
|
||||
[
|
||||
'attrib' => 'isDeleted',
|
||||
'modtype' => LDAP_MODIFY_BATCH_REMOVE_ALL
|
||||
],
|
||||
[
|
||||
'attrib' => 'distinguishedName',
|
||||
'modtype' => LDAP_MODIFY_BATCH_REPLACE,
|
||||
'values' => [$rdn . ',' . $targetDn]
|
||||
],
|
||||
];
|
||||
$success = ldap_modify_batch(getLDAPServerHandle(), $dn, $changes, getCommonLdapControls());
|
||||
if ($success) {
|
||||
return json_encode([]);
|
||||
}
|
||||
$error = getDefaultLDAPErrorString(getLDAPServerHandle());
|
||||
logNewMessage(LOG_ERR, 'Tree view restore node failed: ' . $error);
|
||||
return json_encode([
|
||||
'errorTitle' => htmlspecialchars(sprintf(_('Was unable to restore %s.'), $rdn)),
|
||||
'errorText' => $error
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops processing if DN is invalid.
|
||||
*
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -2497,6 +2497,64 @@ window.lam.treeview.deleteNode = function (tokenName, tokenValue, dn, text, okTe
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a node in tree view.
|
||||
*
|
||||
* @param tokenName security token name
|
||||
* @param tokenValue security token value
|
||||
* @param dn base64 encoded DN
|
||||
* @param text text for dialog body
|
||||
* @param okText text for OK button
|
||||
* @param cancelText text for cancel button
|
||||
* @param title dialog title
|
||||
* @param errorOkText text for OK button in error dialog
|
||||
* @param errorTitle dialog title in case of error
|
||||
*/
|
||||
window.lam.treeview.restoreNode = function (tokenName, tokenValue, dn, text, okText, cancelText, title, errorOkText, errorTitle) {
|
||||
const textSpan = document.querySelector('.treeview-restore-entry');
|
||||
textSpan.innerText = window.atob(text);
|
||||
const dialogContent = document.getElementById('treeview_restore_dlg').cloneNode(true);
|
||||
dialogContent.classList.remove('hidden');
|
||||
const targetDn = document.getElementById('treeview-restore-dn').value;
|
||||
Swal.fire({
|
||||
title: title,
|
||||
confirmButtonText: okText,
|
||||
cancelButtonText: cancelText,
|
||||
showCancelButton: true,
|
||||
html: dialogContent.outerHTML,
|
||||
width: '48em'
|
||||
}).then(result => {
|
||||
if (result.isConfirmed) {
|
||||
let data = new FormData();
|
||||
data.append(tokenName, tokenValue);
|
||||
data.append('dn', dn)
|
||||
data.append('targetDn', targetDn)
|
||||
fetch("../misc/ajax.php?function=treeview&command=restoreNode", {
|
||||
method: 'POST',
|
||||
body: data
|
||||
})
|
||||
.then(async response => {
|
||||
const jsonData = await response.json();
|
||||
window.lam.treeview.checkSession(jsonData);
|
||||
const tree = mar10.Wunderbaum.getTree("#ldap_tree");
|
||||
const parentNode = tree.findKey(dn).parent;
|
||||
await parentNode.setActive();
|
||||
parentNode.loadLazy(true);
|
||||
window.lam.treeview.getNodeContent(tokenName, tokenValue, parentNode.key);
|
||||
if (jsonData['errorTitle']) {
|
||||
const errTextTitle = jsonData['errorTitle'];
|
||||
const textSpanErrorTitle = document.querySelector('.treeview-error-title');
|
||||
textSpanErrorTitle.innerText = errTextTitle;
|
||||
const errText = jsonData['errorText'];
|
||||
const textSpanErrorText = document.querySelector('.treeview-error-text');
|
||||
textSpanErrorText.innerText = errText;
|
||||
window.lam.dialog.showSimpleDialog(errorTitle, null, errorOkText, null, 'treeview_error_dlg');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node content in tree view action area.
|
||||
*
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
namespace LAM\TOOLS\TREEVIEW;
|
||||
use htmlDiv;
|
||||
use htmlForm;
|
||||
use htmlInputField;
|
||||
use htmlJavaScript;
|
||||
use htmlOutputText;
|
||||
use htmlResponsiveInputField;
|
||||
|
@ -142,10 +143,20 @@ function showTree(): void {
|
|||
$deleteDialogDiv = new htmlDiv('treeview_delete_dlg', $deleteDialogContent, ['hidden']);
|
||||
$row->add($deleteDialogDiv);
|
||||
|
||||
$restoreDialogContent = new htmlResponsiveRow();
|
||||
$restoreDialogContent->add(new htmlOutputText(_('Do you really want to restore this entry?')));
|
||||
$restoreDialogContent->addVerticalSpacer('0.5rem');
|
||||
$restoreDialogEntryText = new htmlOutputText('');
|
||||
$restoreDialogEntryText->setCSSClasses(['treeview-restore-entry']);
|
||||
$restoreDialogContent->add($restoreDialogEntryText);
|
||||
$restoreDialogDiv = new htmlDiv('treeview_restore_dlg', $restoreDialogContent, ['hidden']);
|
||||
$row->add($restoreDialogDiv);
|
||||
|
||||
$errorDialogContent = new htmlResponsiveRow();
|
||||
$errorDialogEntryTitle = new htmlOutputText('');
|
||||
$errorDialogEntryTitle->setCSSClasses(['treeview-error-title']);
|
||||
$errorDialogContent->add($errorDialogEntryTitle);
|
||||
$errorDialogContent->addVerticalSpacer('0.5rem');
|
||||
$errorDialogEntryText = new htmlOutputText('');
|
||||
$errorDialogEntryText->setCSSClasses(['treeview-error-text']);
|
||||
$errorDialogContent->add($errorDialogEntryText);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue