diff --git a/src/Symfony/Component/Ldap/Adapter/AdapterInterface.php b/src/Symfony/Component/Ldap/Adapter/AdapterInterface.php index 1e2f72d5bd..cc9a7a63e9 100644 --- a/src/Symfony/Component/Ldap/Adapter/AdapterInterface.php +++ b/src/Symfony/Component/Ldap/Adapter/AdapterInterface.php @@ -34,6 +34,13 @@ interface AdapterInterface */ public function createQuery($dn, $query, array $options = array()); + /** + * Fetches the entry manager instance. + * + * @return EntryManagerInterface + */ + public function getEntryManager(); + /** * Escape a string for use in an LDAP filter or DN. * diff --git a/src/Symfony/Component/Ldap/Adapter/EntryManagerInterface.php b/src/Symfony/Component/Ldap/Adapter/EntryManagerInterface.php new file mode 100644 index 0000000000..b53e2e0662 --- /dev/null +++ b/src/Symfony/Component/Ldap/Adapter/EntryManagerInterface.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Ldap\Adapter; + +use Symfony\Component\Ldap\Entry; + +/** + * Entry manager interface. + * + * @author Charles Sarrazin + */ +interface EntryManagerInterface +{ + /** + * Adds a new entry in the Ldap server. + * + * @param Entry $entry + */ + public function add(Entry $entry); + + /** + * Updates an entry from the Ldap server. + * + * @param Entry $entry + */ + public function update(Entry $entry); + + /** + * Removes an entry from the Ldap server. + * + * @param Entry $entry + */ + public function remove(Entry $entry); +} diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Adapter.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Adapter.php index 4bf0303df0..545d5d69a7 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Adapter.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Adapter.php @@ -21,6 +21,7 @@ class Adapter implements AdapterInterface { private $config; private $connection; + private $entryManager; public function __construct(array $config = array()) { @@ -43,6 +44,18 @@ class Adapter implements AdapterInterface return $this->connection; } + /** + * {@inheritdoc} + */ + public function getEntryManager() + { + if (null === $this->entryManager) { + $this->entryManager = new EntryManager($this->connection); + } + + return $this->entryManager; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php new file mode 100644 index 0000000000..18043ccc99 --- /dev/null +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Ldap\Adapter\ExtLdap; + +use Symfony\Component\Ldap\Adapter\EntryManagerInterface; +use Symfony\Component\Ldap\Entry; +use Symfony\Component\Ldap\Exception\LdapException; + +/** + * @author Charles Sarrazin + */ +class EntryManager implements EntryManagerInterface +{ + private $connection; + + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * {@inheritdoc} + */ + public function add(Entry $entry) + { + $con = $this->connection->getResource(); + + if (!@ldap_add($con, $entry->getDn(), $entry->getAttributes())) { + throw new LdapException(sprintf('Could not add entry "%s": %s', $entry->getDn(), ldap_error($con))); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function update(Entry $entry) + { + $con = $this->connection->getResource(); + + if (!@ldap_modify($con, $entry->getDn(), $entry->getAttributes())) { + throw new LdapException(sprintf('Could not update entry "%s": %s', $entry->getDn(), ldap_error($con))); + } + } + + /** + * {@inheritdoc} + */ + public function remove(Entry $entry) + { + $con = $this->connection->getResource(); + + if (!@ldap_delete($con, $entry->getDn())) { + throw new LdapException(sprintf('Could not remove entry "%s": %s', $entry->getDn(), ldap_error($con))); + } + } +} diff --git a/src/Symfony/Component/Ldap/Entry.php b/src/Symfony/Component/Ldap/Entry.php index f4da743742..3248d96d99 100644 --- a/src/Symfony/Component/Ldap/Entry.php +++ b/src/Symfony/Component/Ldap/Entry.php @@ -59,4 +59,25 @@ class Entry { return $this->attributes; } + + /** + * Sets a value for the given attribute. + * + * @param $name + * @param array $value + */ + public function setAttribute($name, array $value) + { + $this->attributes[$name] = $value; + } + + /** + * Removes a given attribute. + * + * @param $name + */ + public function removeAttribute($name) + { + unset($this->attributes[$name]); + } } diff --git a/src/Symfony/Component/Ldap/Ldap.php b/src/Symfony/Component/Ldap/Ldap.php index 82a443ca57..514f51d22c 100644 --- a/src/Symfony/Component/Ldap/Ldap.php +++ b/src/Symfony/Component/Ldap/Ldap.php @@ -46,6 +46,14 @@ final class Ldap implements LdapInterface return $this->adapter->createQuery($dn, $query, $options); } + /** + * {@inheritdoc} + */ + public function getEntryManager() + { + return $this->adapter->getEntryManager(); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Ldap/LdapInterface.php b/src/Symfony/Component/Ldap/LdapInterface.php index 8f89bd60df..767aa83dd1 100644 --- a/src/Symfony/Component/Ldap/LdapInterface.php +++ b/src/Symfony/Component/Ldap/LdapInterface.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Ldap; +use Symfony\Component\Ldap\Adapter\EntryManagerInterface; use Symfony\Component\Ldap\Adapter\QueryInterface; /** @@ -33,4 +34,9 @@ interface LdapInterface extends BaseLdapInterface * @return QueryInterface */ public function query($dn, $query, array $options = array()); + + /** + * @return EntryManagerInterface + */ + public function getEntryManager(); } diff --git a/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php new file mode 100644 index 0000000000..6bf1003de1 --- /dev/null +++ b/src/Symfony/Component/Ldap/Tests/Adapter/ExtLdap/LdapManagerTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Ldap\Tests; + +use Symfony\Component\Ldap\Adapter\ExtLdap\Adapter; +use Symfony\Component\Ldap\Adapter\ExtLdap\Collection; +use Symfony\Component\Ldap\Entry; +use Symfony\Component\Ldap\Exception\LdapException; + +/** + * @requires extension ldap + */ +class LdapManagerTest extends \PHPUnit_Framework_TestCase +{ + /** @var Adapter */ + private $adapter; + + protected function setUp() + { + $this->adapter = new Adapter(array('host' => 'localhost', 'port' => 3389)); + $this->adapter->getConnection()->bind('cn=admin,dc=symfony,dc=com', 'symfony'); + } + + /** + * @group functional + */ + public function testLdapAddAndRemove() + { + $this->executeSearchQuery(1); + + $entry = new Entry('cn=Charles Sarrazin,dc=symfony,dc=com', array( + 'sn' => array('csarrazi'), + 'objectclass' => array( + 'inetOrgPerson', + ), + )); + + $em = $this->adapter->getEntryManager(); + $em->add($entry); + + $this->executeSearchQuery(2); + + $em->remove($entry); + $this->executeSearchQuery(1); + } + + /** + * @group functional + */ + public function testLdapAddInvalidEntry() + { + $this->setExpectedException(LdapException::class); + $this->executeSearchQuery(1); + + // The entry is missing a subject name + $entry = new Entry('cn=Charles Sarrazin,dc=symfony,dc=com', array( + 'objectclass' => array( + 'inetOrgPerson', + ), + )); + + $em = $this->adapter->getEntryManager(); + $em->add($entry); + } + + /** + * @group functional + */ + public function testLdapUpdate() + { + $result = $this->executeSearchQuery(1); + + $entry = $result[0]; + $this->assertNull($entry->getAttribute('email')); + + $em = $this->adapter->getEntryManager(); + $em->update($entry); + + $result = $this->executeSearchQuery(1); + + $entry = $result[0]; + $this->assertNull($entry->getAttribute('email')); + + $entry->removeAttribute('email'); + $em->update($entry); + + $result = $this->executeSearchQuery(1); + $entry = $result[0]; + $this->assertNull($entry->getAttribute('email')); + } + + /** + * @return Collection|Entry[] + */ + private function executeSearchQuery($expectedResults = 1) + { + $results = $this + ->adapter + ->createQuery('dc=symfony,dc=com', '(objectclass=person)') + ->execute() + ; + + $this->assertCount($expectedResults, $results); + + return $results; + } +}