diff --git a/contrib/openldap/core-fd.schema b/contrib/openldap/core-fd.schema index 070a1ba9f090a47f4662ae17ae86fc0e49f5469b..f7c7ea36371d4704919b9bdb7e9cfd336907f9eb 100644 --- a/contrib/openldap/core-fd.schema +++ b/contrib/openldap/core-fd.schema @@ -2,7 +2,7 @@ ## core-fd.schema - Needed by FusionDirectory for its basic functionalities ## -# Last OID used for attributes : 1.3.6.1.4.1.38414.62.1.67 05/02/24 # +# Last OID used for attributes : 1.3.6.1.4.1.38414.62.1.68 12/03/24 # # Last OID used for objectClass : 1.3.6.1.4.1.38414.62.2.11 29/01/24 # ##### Attributes from gosa ###### @@ -73,6 +73,12 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.51 NAME 'fdSnapshotDataSource' SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) +attributetype ( 1.3.6.1.4.1.38414.62.1.68 NAME 'fdSnapshotHash' + DESC 'FusionDirectory - hash of the current snapShot allowing diff verification with MD5' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) + ##### Subscriptions Attributes ###### attributetype ( 1.3.6.1.4.1.38414.62.11.1 NAME 'fdSubscriptionStartDate' @@ -504,7 +510,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 $ fdSnapshotDataSource ) - MAY ( fdSnapshotObjectType $ description ) ) + MAY ( fdSnapshotObjectType $ description $ fdSnapshotHash) ) ### New FusionDirectory Objectclass ### diff --git a/include/management/snapshot/class_SnapshotHandler.inc b/include/management/snapshot/class_SnapshotHandler.inc index 61744b61dd46db5920a9eff8eac9ff2915ce1ae0..c95078c5a82d78595771f81549b56f5897ab1082 100644 --- a/include/management/snapshot/class_SnapshotHandler.inc +++ b/include/management/snapshot/class_SnapshotHandler.inc @@ -254,6 +254,7 @@ class SnapshotHandler $target['description'] = $description; $target['fdSnapshotObjectType'] = $objectType; $target['fdSnapshotDataSource'] = $snapshotSource; + $target['fdSnapshotHash'] = md5($data); /* Insert the new snapshot But we have to check first, if the given gosaSnapshotTimestamp @@ -380,7 +381,7 @@ class SnapshotHandler $ldap->cd($new_base); $ldap->search( '(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN='.ldap_escape_f($dn).'))', - ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType'], + ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType','fdSnapshotHash'], 'one' ); @@ -416,7 +417,7 @@ class SnapshotHandler $ldap->cd($new_base); $ldap->search( '(objectClass=gosaSnapshotObject)', - ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType'], + ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType', 'fdSnapshotHash'], 'one' ); while ($entry = $ldap->fetch(TRUE)) { diff --git a/plugins/personal/generic/class_user.inc b/plugins/personal/generic/class_user.inc index 08f4f2053bf3fe8230c6e64064b54c325b369f20..f32e0f14017a8f934794dfe539e13cce178d5d63 100644 --- a/plugins/personal/generic/class_user.inc +++ b/plugins/personal/generic/class_user.inc @@ -23,8 +23,8 @@ class PostalAddressAttribute extends TextAreaAttribute function inputValue ($ldapValue) { return str_replace( - ['$', '\24','\5C'], - ["\n", '$', '\\'], + ['$', '\24', '\5C'], + ["\n", '$', '\\'], $ldapValue ); } @@ -36,7 +36,7 @@ class PostalAddressAttribute extends TextAreaAttribute '$', str_replace( ['\\', '$'], - ['\5C','\24'], + ['\5C', '\24'], $this->getValue() ) ); @@ -55,7 +55,7 @@ class user extends simplePlugin 'plIcon' => 'geticon.php?context=applications&icon=user-info&size=48', 'plSmallIcon' => 'geticon.php?context=applications&icon=user-info&size=16', 'plSelfModify' => TRUE, - 'plObjectClass' => ['inetOrgPerson','organizationalPerson','person'], + 'plObjectClass' => ['inetOrgPerson', 'organizationalPerson', 'person'], 'plFilter' => '(objectClass=inetOrgPerson)', 'plObjectType' => ['user' => [ 'name' => _('User'), @@ -65,10 +65,10 @@ class user extends simplePlugin 'icon' => 'geticon.php?context=types&icon=user&size=16', 'ou' => get_ou('userRDN'), ]], - 'plForeignKeys' => [ - 'manager' => ['user','dn','manager=%oldvalue%','*'] + 'plForeignKeys' => [ + 'manager' => ['user', 'dn', 'manager=%oldvalue%', '*'] ], - 'plSearchAttrs' => ['uid','description'], + 'plSearchAttrs' => ['uid', 'description'], 'plProvidedAcls' => array_merge( parent::generatePlProvidedAcls(static::getAttributesInfo()), @@ -82,9 +82,9 @@ class user extends simplePlugin global $config; $languages = Language::getList(TRUE); asort($languages); - $languages = array_merge(['' => ''], $languages); + $languages = array_merge(['' => ''], $languages); $attributesInfo = [ - 'perso' => [ + 'perso' => [ 'name' => _('Personal information'), 'icon' => 'geticon.php?context=types&icon=user&size=16', 'attrs' => [ @@ -115,7 +115,7 @@ class user extends simplePlugin ), ] ], - 'contact' => [ + 'contact' => [ 'name' => _('Organizational contact information'), 'icon' => 'geticon.php?context=types&icon=contact&size=16', 'attrs' => [ @@ -161,7 +161,7 @@ class user extends simplePlugin ), ] ], - 'account' => [ + 'account' => [ 'name' => _('Account information'), 'icon' => 'geticon.php?context=applications&icon=ldap&size=16', 'attrs' => [ @@ -181,7 +181,7 @@ class user extends simplePlugin ), ] ], - 'homecontact' => [ + 'homecontact' => [ 'name' => _('Personal contact information'), 'icon' => 'geticon.php?context=types&icon=contact&size=16', 'attrs' => [ @@ -259,6 +259,7 @@ class user extends simplePlugin function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) { + global $config; parent::__construct($dn, $object, $parent, $mainTab); if ($this->is_template && !$this->initially_was_account) { @@ -274,6 +275,14 @@ class user extends simplePlugin $this->attributesAccess['jpegPhoto']->setPlaceholder(fread($fd, filesize($filename))); $this->was_locked = $this->attributesAccess['userPassword']->isLocked(); + + // Verification is snapshot is enabled and automatic. + // Note : string values are stored in that array. + if (isset($config->current['ENABLEAUTOMATICSNAPSHOTS']) && isset($config->current['ENABLESNAPSHOTS'])) { + if (strtolower($config->current['ENABLEAUTOMATICSNAPSHOTS']) === 'true' && strtolower($config->current['ENABLESNAPSHOTS']) === 'true') { + $this->generateAutomaticSnapshot(); + } + } } function resetCopyInfos () @@ -285,14 +294,14 @@ class user extends simplePlugin private function update_cn () { global $config; - $pattern = $config->get_cfg_value('CnPattern', '%givenName% %sn%'); + $pattern = $config->get_cfg_value('CnPattern', '%givenName% %sn%'); $this->attributesAccess['cn']->setValue($this->applyPattern($pattern)); } private function applyPattern ($pattern) { - $fields = templateHandling::listFields($pattern); - $attrs = []; + $fields = templateHandling::listFields($pattern); + $attrs = []; foreach ($fields as $field) { if (in_array($field, $this->attributes)) { $attrs[$field] = $this->$field; @@ -307,7 +316,7 @@ class user extends simplePlugin } } } - trigger_error('Could not find field '.$field.' in any tab!'); + trigger_error('Could not find field ' . $field . ' in any tab!'); } return templateHandling::parseString($pattern, $attrs); @@ -318,13 +327,13 @@ class user extends simplePlugin global $config; if ($this->is_template) { - return 'cn='.ldap_escape_dn($this->_template_cn).',ou=templates,'.get_ou('userRDN').$this->base; + return 'cn=' . ldap_escape_dn($this->_template_cn) . ',ou=templates,' . get_ou('userRDN') . $this->base; } $this->update_cn(); $attribute = $config->get_cfg_value('accountPrimaryAttribute', 'uid'); - return $this->create_unique_dn($attribute, get_ou('userRDN').$this->base); + return $this->create_unique_dn($attribute, get_ou('userRDN') . $this->base); } public function render (): string @@ -365,8 +374,8 @@ class user extends simplePlugin } if (!$this->is_template && $this->was_locked && $this->attributesAccess['userPassword']->hasChanged()) { - $methods = passwordMethod::get_available_methods(); - $pmethod = new $methods[$this->attributesAccess['userPassword']->getMethod()]($this->dn); + $methods = passwordMethod::get_available_methods(); + $pmethod = new $methods[$this->attributesAccess['userPassword']->getMethod()]($this->dn); $pmethod->lock_account($this->dn); } @@ -383,24 +392,40 @@ class user extends simplePlugin session::set('ui', $ui); session::set('Last_init_lang', 'update'); } - // Verification is snapshot is enabled and automatic. - // Note : string values are stored in that array. - if (isset($config->current['ENABLEAUTOMATICSNAPSHOTS']) && isset($config->current['ENABLESNAPSHOTS'])) { - if (strtolower($config->current['ENABLEAUTOMATICSNAPSHOTS']) === 'true' && strtolower($config->current['ENABLESNAPSHOTS']) === 'true' ) { - $this->generateAutomaticSnapshot(); - } - } return parent::post_save(); } - /* - * Create the snapshot object in case of automated snapshot. + +// public function verifyLastSnapshotMD5 () : BOOL +// { +// +// } + + /** + * @return void + * Note : Create a snapshot of current data before save, verifying if data have changed before taking a snap. */ public function generateAutomaticSnapshot () { - $snapshotHandler = new SnapshotHandler(); - $snapshotHandler->createSnapshot($this->dn, 'automatic snapshot', 'USER', 'FD'); - $snapshotHandler->verifySnapshotRetention($this->dn); + // Get the hash of current data before modifications. + global $config; + $ldap = $config->get_ldap_link(); + $currentSnapHash = md5($ldap->generateLdif($this->dn, '(!(objectClass=gosaDepartment))', 'sub')); + + // Verify if current snap hash already exist in the list of existing snapshots - not taking a snap if it exists. + $snapshotHandler = new SnapshotHandler(); + $existingSnapshots = $snapshotHandler->getAvailableSnapsShots($this->dn); + $sameHashExist = FALSE; + foreach ($existingSnapshots as $snap) { + if (!empty($snap['fdSnapshotHash'][0]) && $snap['fdSnapshotHash'][0] === $currentSnapHash) { + $sameHashExist = TRUE; + } + } + // Create the snapshot + if ($sameHashExist === FALSE) { + $snapshotHandler->createSnapshot($this->dn, 'automatic snapshot', 'USER', 'FD'); + $snapshotHandler->verifySnapshotRetention($this->dn); + } } function adapt_from_template (array $attrs, array $skip = []) @@ -430,7 +455,7 @@ class user extends simplePlugin $ldap = $config->get_ldap_link(); $ldap->cat($userdn, ['pwdPolicySubentry', 'pwdHistory', 'pwdChangedTime', 'userPassword']); - $attrs = $ldap->fetch(TRUE); + $attrs = $ldap->fetch(TRUE); $ppolicydn = ''; if (isset($attrs['pwdPolicySubentry'][0])) { $ppolicydn = $attrs['pwdPolicySubentry'][0]; @@ -482,7 +507,7 @@ class user extends simplePlugin if (isset($policy['pwdMinAge'][0]) && isset($attrs['pwdChangedTime'][0])) { $date = LdapGeneralizedTime::fromString($attrs['pwdChangedTime'][0]); $date->setTimezone(timezone::utc()); - $now = new DateTime('now', timezone::utc()); + $now = new DateTime('now', timezone::utc()); if ($now->getTimeStamp() < $date->getTimeStamp() + $policy['pwdMinAge'][0]) { return sprintf(_('You must wait %d seconds before changing your password again'), $policy['pwdMinAge'][0] - ($now->getTimeStamp() - $date->getTimeStamp())); } @@ -494,7 +519,7 @@ class user extends simplePlugin unset($attrs['pwdHistory']['count']); foreach ($attrs['pwdHistory'] as $pwdHistory) { $pwdHistory = explode('#', $pwdHistory, 4); - $method = passwordMethod::get_method($pwdHistory[3], $user); + $method = passwordMethod::get_method($pwdHistory[3], $user); if (($method !== NULL) && $method->checkPassword($new_password, $pwdHistory[3])) { return _('Password is in history of old passwords'); }