diff --git a/src/Entry.php b/src/Entry.php index 8e71c09..7dd7140 100644 --- a/src/Entry.php +++ b/src/Entry.php @@ -22,9 +22,13 @@ class Entry { /** * Changed data - * @var array> + * @var array{add: array>, replace: array>, delete: array>} */ - protected $changes = array(); + protected $changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(), + ); /** * LDAP connection @@ -88,11 +92,69 @@ class Entry { * @return bool */ public function __isset($key) { - return ( - in_array($key, array('dn', 'changes')) - || array_key_exists($key, $this -> data) - || array_key_exists($key, $this -> changes) - ); + if (in_array($key, array('dn', 'changes'))) + return true; + return $this->attribute_is_set($key); + } + + /** + * Check if entry attribute is set + * @param string $attr + * @return bool + */ + public function attribute_is_set($attr) { + $attr = strtolower($attr); + // Attribute defined in object data and not deleted + if ( + array_key_exists($attr, $this -> data) + && ( + !array_key_exists($attr, $this -> changes['delete']) + || $this -> data[$attr] != $this -> changes['delete'][$attr] + ) + ) + return true; + + // Note: Do not need to to replace changes, because we only replace value on pre-existing attribute + + // Attribute have been added + if (array_key_exists($attr, $this -> changes['add'])) + return true; + + return false; + } + + /** + * Check if the entry could have the specified attribute against the LDAP schema (if loaded) + * @param string $attr + * @return bool + */ + public function has_attribute($attr) { + if (!$this -> ldap || !$this -> ldap -> schema) + return true; + $objectclasses = $this -> get_raw_values('objectclass'); + if (!$objectclasses || !is_array($objectclasses)) + return false; + return $this -> ldap -> schema -> has_attribute($attr, $objectclasses); + } + + /** + * Get raw attribute values + * @param string $attr The expected attribute name + * @return array + */ + protected function _get_raw_values($attr) { + $attr = mb_strtolower($attr); + if (array_key_exists($attr, $this -> changes['replace'])) + return $this -> changes['replace'][$attr]; + $values = array_key_exists($attr, $this -> data)?$this -> data[$attr]:array(); + if (array_key_exists($attr, $this -> changes['add'])) + $values = array_merge($values, $this -> changes['add'][$attr]); + if (array_key_exists($attr, $this -> changes['delete'])) { + foreach ($this -> changes['delete'][$attr] as $value) + if (($key = array_search($value, $values)) !== false) + unset($values[$key]); + } + return $values; } /** @@ -105,12 +167,7 @@ class Entry { * @return ( $all_values is True ? array : mixed ) */ public function get_raw_values($attr, $default=null, $all_values=true) { - $attr = mb_strtolower($attr); - $values = false; - if (array_key_exists($attr, $this -> changes)) - $values = &$this -> changes[$attr]; - elseif (array_key_exists($attr, $this -> data)) - $values = &$this -> data[$attr]; + $values = $this -> _get_raw_values($attr); if (!$values) return is_null($default) && $all_values?array():$default; return $all_values?$values:$values[0]; @@ -129,12 +186,7 @@ class Entry { public function get_value($attr, $default=null, $first=false) { if (!$this -> ldap || !$this -> ldap -> schema) return $this -> get_raw_values($attr, $default, $first?false:true); - $attr = mb_strtolower($attr); - $values = false; - if (array_key_exists($attr, $this -> changes)) - $values = &$this -> changes[$attr]; - elseif (array_key_exists($attr, $this -> data)) - $values = &$this -> data[$attr]; + $values = $this -> _get_raw_values($attr); $attribute = $this -> ldap -> schema -> attribute($attr); if (!$attribute) return $this -> error('Unkown attribute %s in schema', null, $attr); @@ -150,35 +202,204 @@ class Entry { * Set raw LDAP attribute values * @param string $attr * @param mixed $values - * @return void + * @return bool */ public function set_raw_values($attr, $values) { - $attr = mb_strtolower($attr); - if (is_null($values)) - $values = array(); - else if (!is_array($values)) - $values = array($values); - $this -> changes[$attr] = array(); - foreach($values as $value) - $this -> changes[$attr][] = strval($value); + return $this -> replace($attr, $values, true); } /** * Set LDAP attribute values * @param string $attr * @param mixed $value - * @return void|false + * @return bool */ public function set_value($attr, $value) { + return $this -> replace($attr, $value); + } + + /** + * Convert attribute PHP value to LDAP values + * @param string $attr The attribute name + * @param mixed $value + * @param bool $one_value Convert one value of the attribute (optional, default: false) + * @return ( $one_value is True ? string|null : array|false ) + */ + public function php2ldap($attr, $value, $one_value=false) { if (!$this -> ldap || !$this -> ldap -> schema) { - $this -> set_raw_values($attr, $value); - return; + // No LDAP schema available + if ($one_value) + return is_null($value)?null:strval($value); + if (is_null($value)) return array(); + $value = is_array($value)?$value:array($value); + $ldap_values = array(); + foreach($value as $v) + $ldap_values[] = strval($v); + return $ldap_values; } + + // Convert using LDAP schema $attribute = $this -> ldap -> schema -> attribute($attr); if (!$attribute) return $this -> error('Unkown attribute %s in schema', null, $attr); - $values = $attribute -> php2ldap($value); - $this -> set_raw_values($attr, $values); + return $attribute -> php2ldap($value, $one_value); + } + + /** + * Add an attribute value + * @param string $attr The attribute name + * @param mixed $value The value to add. If it's an array, consider as multiple value to add + * @return bool + */ + public function add($attr, $value) { + if (!$this -> has_attribute($attr)) + return $this -> error('%s: This entry could not have the attribute %s', $this, $attr); + if (is_array($value)) { + foreach($value as $v) + if (!$this -> add($attr, $v)) + return false; + return true; + } + $attr = mb_strtolower($attr); + $value = $this -> php2ldap($attr, $value, true); + if (is_null($value)) + return $this -> error('%s: Could not add null value the attribute %s', $this, $attr); + + // If we already do a replace on this attribute, add it in this attribute + if (isset($this -> changes['replace'][$attr])) { + if (in_array($value, $this -> changes['replace'][$attr])) + return true; + $this -> changes['replace'][$attr][] = $value; + return true; + } + + // If we already have a delete operation on this attribute, check this value was not deleted + if (isset($this -> changes['delete'][$attr])) { + if (($key = array_search($value, $this -> changes['delete'][$attr])) !== false) { + unset($this -> changes['delete'][$attr][$key]); + if (empty($this -> changes['delete'][$attr])) + unset($this -> changes['delete'][$attr]); + } + } + + // Check if this value is not already in the attribute data + if (isset($this -> data[$attr]) && in_array($value, $this -> data[$attr])) + return true; + + // Check if this value was not already added + if (isset($this -> changes['add'][$attr])) { + if (in_array($value, $this -> changes['add'][$attr])) + return true; + $this -> changes['add'][$attr][] = $value; + } + else + $this -> changes['add'][$attr] = array($value); + return true; + } + + /** + * Delete an attribute value + * @param string $attr The attribute name + * @param mixed $value The value to delete. If it's an array, consider as multiple value to delete. + * If not provided or null, delete all the attribute name. + * @return bool + */ + public function delete($attr, $value=null) { + if (!$this -> has_attribute($attr)) + return $this -> error('%s: This entry could not have the attribute %s', $this, $attr); + + $attr = mb_strtolower($attr); + + // If null value provided, delete all attribute values + if (is_null($value)) { + // Remove all previous operations on this attribute + if (isset($this -> changes['replace'][$attr])) + unset($this -> changes['replace'][$attr]); + if (isset($this -> changes['add'][$attr])) + unset($this -> changes['add'][$attr]); + if (isset($this -> changes['delete'][$attr])) + unset($this -> changes['delete'][$attr]); + // If attribute originally have data, delete all of it + if (isset($this -> data[$attr])) + $this -> changes['delete'][$attr] = $this -> data[$attr]; + return true; + } + + $values = $this -> php2ldap($attr, $value); + if ($values === false) + return false; + + foreach($values as $value) { + // If we already do a replace operation on this attribute, delete the value in the replaced value + if (isset($this -> changes['replace'][$attr])) { + if (($key = array_search($value, $this -> changes['replace'][$attr])) !== false) { + // Value detected in replaced value, delete if + unset($this -> changes['replace'][$attr][$key]); + // Check if we still have replace values + if (empty($this -> changes['replace'][$attr])) { + // No replaced value, consider the operation as a deletion + unset($this -> changes['replace'][$attr]); + if (isset($this -> data[$attr])) + $this -> changes['delete'][$attr] = $this -> data[$attr]; + } + } + continue; + } + + // If we already have a delete operation on this attribute, check this value was not already deleted + if (isset($this -> changes['delete'][$attr]) && in_array($value, $this-> changes['delete'][$attr])) + continue; + + // Check this value is in the attribute data + if (!isset($this -> data[$attr]) || !in_array($value, $this -> data[$attr])) + continue; + + if (isset($this -> changes['delete'][$attr])) + $this -> changes['delete'][$attr][] = $value; + else + $this -> changes['delete'][$attr] = array($value); + } + return true; + } + + /** + * Replace an attribute value + * @param string $attr The attribute name + * @param mixed $value The new attribute value to delete. If null, delete all the attribute name. + * @param bool $consider_as_raw_values If true, consider provided value as raw (optional, default: false) + * @return bool + */ + public function replace($attr, $value, $consider_as_raw_values=false) { + if (!$this -> has_attribute($attr)) + return $this -> error('%s: This entry could not have the attribute %s', $this, $attr); + $attr = mb_strtolower($attr); + + // If raw values provied, just be sure it's an array of string + if ($consider_as_raw_values) { + $value = Util::ensure_array_of_string($value); + } + else { + // Convert PHP value to LDAP values + $value = $this -> php2ldap($attr, $value); + } + + // If empty value, delete the attribute + if (empty($value)) + return $this -> delete($attr); + + // Remove all previous add/delete operations on this attribute + if (isset($this -> changes['add'][$attr])) + unset($this -> changes['add'][$attr]); + if (isset($this -> changes['delete'][$attr])) + unset($this -> changes['delete'][$attr]); + if (isset($this -> changes['replace'][$attr])) + unset($this -> changes['replace'][$attr]); + + if (!isset($this->data[$attr])) + $this -> changes['add'][$attr] = $value; + elseif ($value != $this->data[$attr]) + $this -> changes['replace'][$attr] = $value; + return true; } /** @@ -199,4 +420,14 @@ class Entry { error_log($error); throw new LdapException($error); // @phpstan-ignore-line } + + /** + * Magic method to compute string representation of this schema entry object + * @return string + */ + public function __toString() { + if ($this -> dn) + return sprintf('', $this->dn); + return ''; + } } diff --git a/src/Util.php b/src/Util.php new file mode 100644 index 0000000..76b0d5f --- /dev/null +++ b/src/Util.php @@ -0,0 +1,21 @@ + + */ + public static function ensure_array_of_string($values) { + $result = array(); + if (!is_null($values)) { + $values = is_array($values)?$values:array($values); + foreach ($values as $value) + $result[] = strval($value); + } + return $result; + } +} diff --git a/tests/EntryTest.php b/tests/EntryTest.php new file mode 100644 index 0000000..fa4ee99 --- /dev/null +++ b/tests/EntryTest.php @@ -0,0 +1,858 @@ +assertEquals($dn, $entry->dn); + } + + /** + * @covers \EesyLDAP\Entry::__get + */ + public function testGetChanges() { + $entry = new Entry(); + $attr = 'cn'; + $value = 'test'; + $expected_changes = array( + 'add' => array($attr => array($value)), + 'replace' => array(), + 'delete' => array(), + ); + $entry->add($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::__get + */ + public function testGetAttribute() { + $attr = 'cn'; + $values = array('test'); + $entry = new Entry(null, array($attr => $values)); + $this->assertEquals($values, $entry->__get($attr)); + } + + /** + * @covers \EesyLDAP\Entry::__set + */ + public function testSetDn() { + $entry = new Entry(); + $dn = 'cn=admin'; + $entry->dn = $dn; + $this->assertEquals($dn, $entry->dn); + } + + /** + * @covers \EesyLDAP\Entry::__set + */ + public function testSetDnAsFalse() { + $dn = 'cn=admin'; + $entry = new Entry($dn); + $entry->dn = false; + $this->assertEquals(false, $entry->dn); + } + + /** + * @covers \EesyLDAP\Entry::__set + */ + public function testSetDnWithBadValue() { + $entry = new Entry(); + $this->expectException(LdapException::class); + // @phpstan-ignore-next-line + $entry->dn = array(); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testDnIsSet() { + $entry = new Entry(); + $this->assertTrue(isset($entry->dn)); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testChangesIsSet() { + $entry = new Entry(); + $this->assertTrue(isset($entry->changes)); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testAttributeIsSetWithMagicMethod() { + $attr = 'cn'; + $entry = new Entry(null, array($attr => array('value'))); + $this->assertTrue($entry->__isset($attr)); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testAttributeIsNotSetWithMagicMethod() { + $entry = new Entry(); + $this->assertFalse(isset($entry->undefined)); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testAttributeIsSet() { + $attr = 'cn'; + $entry = new Entry(null, array($attr => array('value'))); + $this->assertTrue($entry->attribute_is_set($attr)); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testAttributeIsNotSet() { + $entry = new Entry(); + $this->assertFalse($entry->attribute_is_set('undefined')); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testAttributeIsSetAfterDelete() { + $attr = 'cn'; + $value = 'test'; + $entry = new Entry(null, array($attr => array($value))); + $entry -> delete($attr, $value); + $this->assertFalse($entry->attribute_is_set($attr)); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testAttributeIsSetAfterDeleteOnlyOneOfValue() { + $attr = 'cn'; + $values = array('test1', 'test2'); + $entry = new Entry(null, array($attr => $values)); + $entry -> delete($attr, $values[0]); + $this->assertTrue($entry->attribute_is_set($attr)); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testAttributeIsSetAfterReplace() { + $attr = 'cn'; + $original_value = 'test1'; + $value = 'test2'; + $entry = new Entry(null, array($attr => array($original_value))); + $entry -> replace($attr, $value); + $this->assertTrue($entry->attribute_is_set($attr)); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testAttributeIsSetAfterDeleteAndReplace() { + $attr = 'cn'; + $original_value = 'test1'; + $value = 'test2'; + $entry = new Entry(null, array($attr => array($original_value))); + $entry -> delete($attr, $original_value); + $entry -> replace($attr, $value); + $this->assertTrue($entry->attribute_is_set($attr)); + } + + /** + * @covers \EesyLDAP\Entry::__isset + */ + public function testAttributeIsSetAfterAdd() { + $attr = 'cn'; + $value = 'test'; + $entry = new Entry(); + $entry -> add($attr, $value); + $this->assertTrue($entry->attribute_is_set($attr)); + } + + /** + * @covers \EesyLDAP\Entry::add + */ + public function testAddValueOnNewEntry() { + $entry = new Entry(); + $attr = 'CN'; + $value = 'test'; + $expected_changes = array( + 'add' => array(strtolower($attr) => array($value)), + 'replace' => array(), + 'delete' => array(), + ); + $entry -> add($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::add + */ + public function testAddValuesOnNewEntry() { + $entry = new Entry(); + $attr = 'cn'; + $values = array('test1', 'test2'); + $expected_changes = array( + 'add' => array($attr => $values), + 'replace' => array(), + 'delete' => array(), + ); + $entry -> add($attr, $values); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::add + */ + public function testAddNull() { + $entry = new Entry(); + $this->expectException(LdapException::class); + $entry -> add('cn', null); + } + + /** + * @covers \EesyLDAP\Entry::add + */ + public function testAddExistingValuesOnNewEntry() { + $attr = 'cn'; + $values = array('test1', 'test2'); + $entry = new Entry(null, array($attr => $values)); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(), + ); + $entry -> add($attr, $values); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::add + */ + public function testAddValueMultipleTimeOnNewEntry() { + $entry = new Entry(); + $attr = 'CN'; + $value = 'test'; + $expected_changes = array( + 'add' => array(strtolower($attr) => array($value)), + 'replace' => array(), + 'delete' => array(), + ); + $entry -> add($attr, $value); + $entry -> add($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::add + */ + public function testAddValueAfterReplaceOnNewEntry() { + $entry = new Entry(); + $attr = 'cn'; + $replace_value = 'test1'; + $value = 'test2'; + $expected_changes = array( + 'add' => array($attr => array($replace_value, $value)), + 'replace' => array(), + 'delete' => array(), + ); + $entry -> replace($attr, $replace_value); + $entry -> add($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::add + */ + public function testAddExistingValueAfterReplace() { + $attr = 'cn'; + $replace_value = 'test1'; + $add_value = 'test2'; + $entry = new Entry(null, array($attr => array('original'))); + $expected_changes = array( + 'add' => array(), + 'replace' => array($attr => array($replace_value, $add_value)), + 'delete' => array(), + ); + $entry -> replace($attr, $replace_value); + $entry -> add($attr, $add_value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::add + */ + public function testAddExistingValueAfterReplaceWithSameValue() { + $attr = 'cn'; + $value = 'test'; + $entry = new Entry(null, array($attr => array('original'))); + $expected_changes = array( + 'add' => array(), + 'replace' => array($attr => array($value)), + 'delete' => array(), + ); + $entry -> replace($attr, $value); + $entry -> add($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::add + */ + public function testAddValueAfterDeleteOnNewEntry() { + $attr = 'cn'; + $value = 'test'; + $entry = new Entry(null, array($attr => array($value))); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(), + ); + $entry -> delete($attr, $value); + $entry -> add($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::add + */ + public function testAddValueOnBadAttribute() { + $attr = 'cn'; + $values = array('test1', 'test2'); + $entry = $this->getMockBuilder(Entry::class) + ->disableOriginalConstructor() + ->onlyMethods(['has_attribute']) + ->getMock(); + $entry->expects($this->once()) + ->method('has_attribute') + ->with($attr) + ->willReturn(false); + $this->expectException(LdapException::class); + $entry->add($attr, $values); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteValue() { + $attr = 'CN'; + $value = 'test'; + $entry = new Entry(null, array($attr => array($value))); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(strtolower($attr) => array($value)), + ); + $entry -> delete($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteUndefinedValue() { + $attr = 'cn'; + $value = 'test1'; + $undefined_value = 'test2'; + $entry = new Entry(null, array($attr => array($value))); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(), + ); + $entry -> delete($attr, $undefined_value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteValues() { + $attr = 'CN'; + $values = array('test1', 'test2'); + $entry = new Entry(null, array($attr => $values)); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(strtolower($attr) => $values), + ); + $entry -> delete($attr, $values); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteAttribute() { + $attr = 'CN'; + $values = array('test1', 'test2'); + $entry = new Entry(null, array($attr => $values)); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(strtolower($attr) => $values), + ); + $entry -> delete($attr); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteAttributeAfterReplace() { + $attr = 'CN'; + $values = array('test1', 'test2'); + $entry = new Entry(null, array($attr => $values)); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(strtolower($attr) => $values), + ); + $entry -> replace($attr, 'test3'); + $entry -> delete($attr); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteUniqueValueAfterReplace() { + $attr = 'CN'; + $values = array('test1', 'test2'); + $replace_value = 'test3'; + $entry = new Entry(null, array($attr => $values)); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(strtolower($attr) => $values), + ); + $entry -> replace($attr, $replace_value); + $entry -> delete($attr, $replace_value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteAttributeAfterAdd() { + $attr = 'CN'; + $values = array('test1', 'test2'); + $entry = new Entry(null, array($attr => $values)); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(strtolower($attr) => $values), + ); + $entry -> add($attr, 'test3'); + $entry -> delete($attr); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteAttributeAfterDelete() { + $attr = 'CN'; + $values = array('test1', 'test2'); + $entry = new Entry(null, array($attr => $values)); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(strtolower($attr) => $values), + ); + $entry -> delete($attr, $values[0]); + $entry -> delete($attr); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteUnsetAttribute() { + $attr = 'CN'; + $values = array('test1', 'test2'); + $entry = new Entry(); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(), + ); + $entry -> delete($attr); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteValueAfterReplace() { + $attr = 'cn'; + $replace_values = array('test1', 'test2'); + $value = end($replace_values); + $entry = new Entry(null, array($attr => array('test3'))); + $expected_changes = array( + 'add' => array(), + 'replace' => array($attr => array_slice($replace_values, 0, -1)), + 'delete' => array(), + ); + $entry -> replace($attr, $replace_values); + $entry -> delete($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteUnsetValueAfterReplace() { + $attr = 'cn'; + $replace_values = array('test1', 'test2'); + $value = 'test3'; + $entry = new Entry(null, array($attr => array('test4'))); + $expected_changes = array( + 'add' => array(), + 'replace' => array($attr => $replace_values), + 'delete' => array(), + ); + $entry -> replace($attr, $replace_values); + $entry -> delete($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteValueOnBadAttribute() { + $attr = 'cn'; + $entry = $this->getMockBuilder(Entry::class) + ->disableOriginalConstructor() + ->onlyMethods(['has_attribute']) + ->getMock(); + $entry->expects($this->once()) + ->method('has_attribute') + ->with($attr) + ->willReturn(false); + $this->expectException(LdapException::class); + $entry->delete($attr); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteBadPhpValue() { + $attr = 'cn'; + $value = 'test'; + $entry = $this->getMockBuilder(Entry::class) + ->disableOriginalConstructor() + ->onlyMethods(['php2ldap']) + ->getMock(); + $entry->expects($this->once()) + ->method('php2ldap') + ->with($attr, $value) + ->willReturn(false); + $this->assertFalse($entry->delete($attr, $value)); + } + + /** + * @covers \EesyLDAP\Entry::delete + */ + public function testDeleteAlreadyDeletedValue() { + $attr = 'cn'; + $value = 'test'; + $entry = new Entry(null, array($attr => array($value))); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array($attr => array($value)), + ); + $entry -> delete($attr, $value); + $entry -> delete($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::replace + */ + public function testReplaceValue() { + $attr = 'cn'; + $value = 'test1'; + $entry = new Entry(null, array($attr => array('test2'))); + $expected_changes = array( + 'add' => array(), + 'replace' => array($attr => array($value)), + 'delete' => array(), + ); + $entry -> replace($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::replace + */ + public function testReplaceUndefinedAttributeValue() { + $attr = 'cn'; + $value = 'test1'; + $entry = new Entry(); + $expected_changes = array( + 'add' => array($attr => array($value)), + 'replace' => array(), + 'delete' => array(), + ); + $entry -> replace($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::replace + * @covers EesyLDAP\Util::ensure_array_of_string + */ + public function testReplaceRawValue() { + $attr = 'cn'; + $value = 'test1'; + $entry = new Entry(null, array($attr => array('test2'))); + $expected_changes = array( + 'add' => array(), + 'replace' => array($attr => array($value)), + 'delete' => array(), + ); + $entry -> replace($attr, $value, true); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::replace + */ + public function testReplaceValueWithNull() { + $attr = 'cn'; + $values = array('test'); + $entry = new Entry(null, array($attr => $values)); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array($attr => $values), + ); + $entry -> replace($attr, null); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::replace + */ + public function testReplaceAfterAdd() { + $attr = 'cn'; + $add_value = 'test1'; + $value = 'test2'; + $entry = new Entry(null, array($attr => array('test3'))); + $expected_changes = array( + 'add' => array(), + 'replace' => array($attr => array($value)), + 'delete' => array(), + ); + $entry -> add($attr, $add_value); + $entry -> replace($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::replace + */ + public function testReplaceAfterDelete() { + $attr = 'cn'; + $value = 'test'; + $entry = new Entry(null, array($attr => array('original value'))); + $expected_changes = array( + 'add' => array(), + 'replace' => array($attr => array($value)), + 'delete' => array(), + ); + $entry -> delete($attr); + $entry -> replace($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::replace + */ + public function testReplaceAfterReplace() { + $attr = 'cn'; + $value1 = 'test1'; + $value2 = 'test2'; + $entry = new Entry(null, array($attr => array('original value'))); + $expected_changes = array( + 'add' => array(), + 'replace' => array($attr => array($value2)), + 'delete' => array(), + ); + $entry -> replace($attr, $value1); + $entry -> replace($attr, $value2); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::replace + */ + public function testReplaceWithSameValue() { + $attr = 'cn'; + $value = 'test1'; + $entry = new Entry(null, array($attr => array($value))); + $expected_changes = array( + 'add' => array(), + 'replace' => array(), + 'delete' => array(), + ); + $entry -> replace($attr, $value); + $this->assertEquals($expected_changes, $entry->changes); + } + + /** + * @covers \EesyLDAP\Entry::replace + */ + public function testReplaceValueOnBadAttribute() { + $attr = 'cn'; + $value = 'test'; + $entry = $this->getMockBuilder(Entry::class) + ->disableOriginalConstructor() + ->onlyMethods(['has_attribute']) + ->getMock(); + $entry->expects($this->once()) + ->method('has_attribute') + ->with($attr) + ->willReturn(false); + $this->expectException(LdapException::class); + $entry->replace($attr, $value); + } + + /** + * @covers \EesyLDAP\Entry::get_raw_values + */ + public function testGetRawValues() { + $attr = 'cn'; + $values = array('test1'); + $entry = new Entry(null, array($attr => $values)); + $this->assertEquals($values, $entry->get_raw_values($attr)); + } + + /** + * @covers \EesyLDAP\Entry::get_raw_values + */ + public function testGetRawValuesOnUndefinedAttribute() { + $entry = new Entry(); + $this->assertEquals(array(), $entry->get_raw_values('undefined')); + } + + /** + * @covers \EesyLDAP\Entry::get_raw_values + */ + public function testGetRawValuesAfterAdd() { + $attr = 'cn'; + $values = array('test1'); + $add_value = 'test2'; + $entry = new Entry(null, array($attr => $values)); + $entry->add($attr, $add_value); + $this->assertEquals(array_merge($values, array($add_value)), $entry->get_raw_values($attr)); + } + + /** + * @covers \EesyLDAP\Entry::get_raw_values + */ + public function testGetRawValuesAfterReplace() { + $attr = 'cn'; + $values = array('test1'); + $replace_value = 'test2'; + $entry = new Entry(null, array($attr => $values)); + $entry->replace($attr, $replace_value); + $this->assertEquals(array($replace_value), $entry->get_raw_values($attr)); + } + + /** + * @covers \EesyLDAP\Entry::get_raw_values + */ + public function testGetRawValuesAfterDelete() { + $attr = 'cn'; + $values = array('test1'); + $entry = new Entry(null, array($attr => $values)); + $entry->delete($attr); + $this->assertEquals(array(), $entry->get_raw_values($attr)); + } + + /** + * @covers \EesyLDAP\Entry::get_raw_values + */ + public function testGetRawValuesWithDefault() { + $default = 'default'; + $entry = new Entry(); + $this->assertEquals($default, $entry->get_raw_values('undefined', $default)); + } + + /** + * @covers \EesyLDAP\Entry::get_raw_values + */ + public function testGetRawValuesOnlyFirst() { + $attr = 'cn'; + $values = array('test1', 'test2'); + $entry = new Entry(null, array($attr => $values)); + $this->assertEquals($values[0], $entry->get_raw_values($attr, null, false)); + } + + /** + * @covers \EesyLDAP\Entry::set_raw_values + */ + public function testSetRawValues() { + $attr = 'cn'; + $values = array('test1', 'test2'); + $entry = $this->getMockBuilder(Entry::class) + ->disableOriginalConstructor() + ->onlyMethods(['replace']) + ->getMock(); + $entry->expects($this->once()) + ->method('replace') + ->with($attr, $values, true) + ->willReturn(true); + $this->assertTrue($entry->set_raw_values($attr, $values)); + } + + /** + * @covers \EesyLDAP\Entry::set_value + */ + public function testSetValue() { + $attr = 'cn'; + $values = array('test1', 'test2'); + $entry = $this->getMockBuilder(Entry::class) + ->disableOriginalConstructor() + ->onlyMethods(['replace']) + ->getMock(); + $entry->expects($this->once()) + ->method('replace') + ->with($attr, $values) + ->willReturn(true); + $this->assertTrue($entry->set_value($attr, $values)); + } + + /** + * @covers \EesyLDAP\Entry::__toString + */ + public function testToString() { + $entry = new Entry(); + $this->assertEquals('', $entry->__toString()); + } + + /** + * @covers \EesyLDAP\Entry::__toString + */ + public function testToStringWithDN() { + $dn = 'cn=test'; + $entry = new Entry($dn); + $this->assertEquals("", $entry->__toString()); + } +}