Commit b6468a56 authored by Côme Chilliet's avatar Côme Chilliet

feat(snapshots) Automatically open/save object after snapshot restore

This is an attempt at putting restored object through FD open/save cycle
 to make sure they save without error and trigger any hook that may look
 for changes.
Also store object type in the snapshot LDAP node, even if it is unused
 at the moment.

issue #5715
parent 3b51520d
......@@ -57,6 +57,12 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.3 NAME 'fdLockTimestamp'
ORDERING generalizedTimeOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
attributetype ( 1.3.6.1.4.1.38414.62.1.4 NAME 'fdSnapshotObjectType'
DESC 'FusionDirectory - object type of the snapshotted object'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
# Classes
objectclass ( 1.3.6.1.4.1.10098.1.2.1.19.4 NAME 'gosaDepartment' SUP top AUXILIARY
......@@ -84,7 +90,7 @@ objectclass ( 1.3.6.1.4.1.10098.1.2.1.19.19 NAME 'gosaSnapshotObject'
DESC 'GOsa - Container object for undo and snapshot data'
SUP top STRUCTURAL
MUST ( gosaSnapshotTimestamp $ gosaSnapshotDN $ gosaSnapshotData )
MAY ( description ) )
MAY ( fdSnapshotObjectType $ description ) )
objectclass ( 1.3.6.1.4.1.38414.62.2.1 NAME 'fdLockEntry' SUP top STRUCTURAL
DESC 'FusionDirectory - Class for FD locking'
......
......@@ -1037,27 +1037,8 @@ class LDAP
return @ldap_read($this->cid, $dn, "(objectClass=*)", ["objectClass"]);
}
/*!
* \brief Function to imports ldifs
*
* If DeleteOldEntries is TRUE, the destination entry will be deleted first.
* If JustModify is TRUE the destination entry will only be touched by the attributes specified in the ldif.
* if JustMofify is FALSE the destination dn will be overwritten by the new ldif.
*
* \param integer $srp
*
* \param string $str_attr
*
* \param boolean $JustModify
*
* \param boolean $DeleteOldEntries
*/
function import_complete_ldif ($srp, $str_attr, $JustModify, $DeleteOldEntries)
function parseLdif (string $str_attr): array
{
if ($this->reconnect) {
$this->connect();
}
/* First we split the string into lines */
$fileLines = preg_split("/\n/", $str_attr);
if (end($fileLines) != '') {
......@@ -1126,6 +1107,32 @@ class LDAP
}
}
return $entries;
}
/*!
* \brief Function to imports ldifs
*
* If DeleteOldEntries is TRUE, the destination entry will be deleted first.
* If JustModify is TRUE the destination entry will only be touched by the attributes specified in the ldif.
* if JustMofify is FALSE the destination dn will be overwritten by the new ldif.
*
* \param integer $srp
*
* \param string $str_attr
*
* \param boolean $JustModify
*
* \param boolean $DeleteOldEntries
*/
function import_complete_ldif ($srp, $str_attr, $JustModify, $DeleteOldEntries)
{
$entries = $this->parseLdif($str_attr);
if ($this->reconnect) {
$this->connect();
}
foreach ($entries as $startLine => $entry) {
/* Delete before insert */
$usermdir = ($this->dn_exists($entry['dn']) && $DeleteOldEntries);
......
......@@ -1055,7 +1055,7 @@ class management
function createSnapshotDialog (array $action)
{
global $config, $ui;
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $action['targets'], 'Snaptshot creation initiated!');
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $action['targets'], 'Snapshot creation initiated!');
$this->currentDn = array_pop($action['targets']);
if (empty($this->currentDn)) {
......@@ -1093,7 +1093,7 @@ class management
}
if ($ui->allow_snapshot_restore($this->currentDn, $aclCategory, empty($action['targets']))) {
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Snaptshot restoring initiated!');
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Snapshot restoring initiated!');
$this->dialogObject = new SnapshotRestoreDialog($this->currentDn, $this, empty($action['targets']), $aclCategory);
} else {
msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to restore a snapshot for %s.'), $this->currentDn),
......@@ -1176,13 +1176,14 @@ class management
function createSnapshot (string $dn, string $description)
{
global $ui;
if ($this->currentDn !== $dn) {
if (empty($dn) || ($this->currentDn !== $dn)) {
trigger_error('There was a problem with the snapshot workflow');
return;
}
if (!empty($dn) && $ui->allow_snapshot_create($dn, $this->dialogObject->aclCategory)) {
$this->snapHandler->createSnapshot($dn, $description);
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snaptshot created!');
$entry = $this->listing->getEntry($dn);
if ($entry->snapshotCreationAllowed()) {
$this->snapHandler->createSnapshot($dn, $description, $entry->type);
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot created!');
} else {
msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn),
ERROR_DIALOG);
......@@ -1198,9 +1199,20 @@ class management
{
global $ui;
if (!empty($dn) && $ui->allow_snapshot_restore($dn, $this->dialogObject->aclCategory, $this->dialogObject->global)) {
$this->snapHandler->restoreSnapshot($dn);
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snaptshot restored');
$dn = $this->snapHandler->restoreSnapshot($dn);
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot restored');
$this->closeDialogs();
if ($dn !== FALSE) {
$this->listing->focusDn($dn);
$entry = $this->listing->getEntry($dn);
$this->currentDn = $dn;
set_object_info($this->currentDn);
add_lock($this->currentDn, $ui->dn);
// Open object
$this->openTabObject(objects::open($this->currentDn, $entry->getTemplatedType()), $this->currentDn);
$this->saveChanges();
}
} else {
msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn),
ERROR_DIALOG);
......@@ -1217,7 +1229,7 @@ class management
global $ui;
if (!empty($dn) && $ui->allow_snapshot_delete($dn, $this->dialogObject->aclCategory)) {
$this->snapHandler->removeSnapshot($dn);
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snaptshot deleted');
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot deleted');
} else {
msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to delete a snapshot for %s.'), $dn),
ERROR_DIALOG);
......
......@@ -462,16 +462,7 @@ class managementListing
/* Pre-render list to init things if a dn is gonna be opened on first load */
$dn = urldecode($_REQUEST['dn']);
$action = $m[1];
/* Detect the longer base valid for this dn */
$longerBase = '';
foreach (array_keys($this->bases) as $base) {
if (preg_match('/'.preg_quote($base, '/').'$/i', $dn)
&& (strlen($base) > strlen($longerBase))) {
$longerBase = $base;
}
}
$this->setBase($longerBase);
$this->update($dn);
$this->focusDn($dn);
$this->render();
$result['action'] = $action;
......@@ -527,6 +518,23 @@ class managementListing
return $result;
}
/*!
* \brief Set base close to this dn and load only him
*/
function focusDn (string $dn)
{
/* Detect the longer base valid for this dn */
$longerBase = '';
foreach (array_keys($this->bases) as $base) {
if (preg_match('/'.preg_quote($base, '/').'$/i', $dn)
&& (strlen($base) > strlen($longerBase))) {
$longerBase = $base;
}
}
$this->setBase($longerBase);
$this->update($dn);
}
/*!
* \brief Refresh the bases list
*/
......
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2003-2010 Cajus Pollmeier
Copyright (C) 2011-2016 FusionDirectory
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
......@@ -190,13 +191,15 @@ class SnapshotHandler
*
* \param string $dn The DN
*
* \param array $description Snapshot description
* \param string $description Snapshot description
*
* \param string $objectType Type of snapshotted object
*/
function createSnapshot ($dn, $description = [])
function createSnapshot ($dn, string $description, string $objectType)
{
global $config;
if (!$this->enabled()) {
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snaptshot are disabled but tried to create snapshot');
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot are disabled but tried to create snapshot');
return;
}
......@@ -230,12 +233,13 @@ class SnapshotHandler
}
}
$target = [];
$target = [];
$target['objectClass'] = ['top', 'gosaSnapshotObject'];
$target['gosaSnapshotData'] = gzcompress($data, 6);
$target['gosaSnapshotDN'] = $dn;
$target['description'] = $description;
$target['objectClass'] = ['top', 'gosaSnapshotObject'];
$target['gosaSnapshotData'] = gzcompress($data, 6);
$target['gosaSnapshotDN'] = $dn;
$target['description'] = $description;
$target['fdSnapshotObjectType'] = $objectType;
/* Insert the new snapshot
But we have to check first, if the given gosaSnapshotTimestamp
......@@ -299,7 +303,7 @@ class SnapshotHandler
$ldap->cd($new_base);
$ldap->search(
'(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN='.ldap_escape_f($dn).'))',
['gosaSnapshotTimestamp','gosaSnapshotDN','description'],
['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType'],
'one'
);
......@@ -335,7 +339,7 @@ class SnapshotHandler
$ldap->cd($new_base);
$ldap->search(
'(objectClass=gosaSnapshotObject)',
['gosaSnapshotTimestamp','gosaSnapshotDN','description'],
['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType'],
'one'
);
while ($entry = $ldap->fetch()) {
......@@ -371,34 +375,37 @@ class SnapshotHandler
{
global $config;
if (!$this->enabled()) {
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snaptshot are disabled but tried to restore snapshot');
return [];
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot are disabled but tried to restore snapshot');
return FALSE;
}
$ldap = $config->get_ldap_link();
/* Get the snapshot */
$ldap->cat($dn, ['gosaSnapshotData'], '(gosaSnapshotData=*)');
$ldap->cat($dn, ['gosaSnapshotData','gosaSnapshotDN','fdSnapshotObjectType'], '(gosaSnapshotData=*)');
if ($attrs = $ldap->fetch()) {
/* Prepare import string */
$data = gzuncompress($attrs['gosaSnapshotData'][0]);
if ($data === FALSE) {
msg_dialog::display(_('Error'), _('There was a problem uncompressing snapshot data'), ERROR_DIALOG);
return [];
return FALSE;
}
} else {
msg_dialog::display(_('Error'), _('Snapshot data could not be fetched'), ERROR_DIALOG);
return [];
return FALSE;
}
/* Import the given data */
try {
$ldap->import_complete_ldif($data, FALSE, FALSE);
if (!$ldap->success()) {
msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, "", get_class()), LDAP_ERROR);
msg_dialog::display(_('LDAP error'), msgPool::ldaperror($ldap->get_error(), $dn, '', get_class()), LDAP_ERROR);
return FALSE;
}
return $attrs['gosaSnapshotDN'][0];
} catch (LDIFImportException $e) {
msg_dialog::display(_('LDAP error'), $e->getMessage(), ERROR_DIALOG);
return FALSE;
}
}
}
......@@ -1107,7 +1107,7 @@ class simpleManagement
function createSnapshotDialog ($action, array $target)
{
global $config, $ui;
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $target, 'Snaptshot creation initiated!');
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $target, 'Snapshot creation initiated!');
if (count($target) == 1) {
$this->dn = array_pop($target);
......@@ -1163,7 +1163,7 @@ class simpleManagement
}
if ($ui->allow_snapshot_restore($this->dn, $aclCategory, !count($target))) {
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->dn, 'Snaptshot restoring initiated!');
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->dn, 'Snapshot restoring initiated!');
$this->dialogObject = new SnapshotRestoreDialog($this->dn, $this, !count($target), $aclCategory);
} else {
msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to restore a snapshot for %s.'), $this->dn),
......@@ -1244,7 +1244,7 @@ class simpleManagement
}
if (!empty($dn) && $ui->allow_snapshot_create($dn, $this->dialogObject->aclCategory)) {
$this->snapHandler->createSnapshot($dn, $description);
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snaptshot created!');
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot created!');
} else {
msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn),
ERROR_DIALOG);
......@@ -1261,7 +1261,7 @@ class simpleManagement
global $ui;
if (!empty($dn) && $ui->allow_snapshot_restore($dn, $this->dialogObject->aclCategory, $this->dialogObject->global)) {
$this->snapHandler->restoreSnapshot($dn);
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snaptshot restored');
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot restored');
$this->closeDialogs();
} else {
msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn),
......@@ -1317,7 +1317,7 @@ class simpleManagement
global $ui;
if (!empty($dn) && $ui->allow_snapshot_delete($dn, $this->dialogObject->aclCategory)) {
$this->snapHandler->removeSnapshot($dn);
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snaptshot deleted');
@DEBUG(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot deleted');
} else {
msg_dialog::display(_('Permission'), sprintf(_('You are not allowed to delete a snapshot for %s.'), $dn),
ERROR_DIALOG);
......
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