diff --git a/lam/lib/import.inc b/lam/lib/import.inc index 9110d089..d96475f1 100644 --- a/lam/lib/import.inc +++ b/lam/lib/import.inc @@ -82,6 +82,9 @@ class Importer { $currentEntry[sizeof($currentEntry) - 1] .= substr($line, 1); } } + elseif ($line === '-') { + $currentEntry[] = $line; + } else { $parts = explode(':', $line, 2); if (sizeof($parts) < 2) { @@ -284,6 +287,9 @@ class Importer { } return new DeleteEntryTask($dn); } + elseif ($type !== 'modify') { + throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . ' - changeType: ' . htmlspecialchars($type)); + } $changes = array(); $subtasks = array(); $currentLines = array(); @@ -291,18 +297,14 @@ class Importer { for ($i = 0; $i < $linesCount; $i++) { $line = $entry[$i]; if ($line === '-') { - $subtasks[] = $this->getChangeTypeTask($dn, $currentLines, $type); + $subtasks[] = $this->getChangeTypeTask($dn, $currentLines); $currentLines = array(); - $i++; - $line = $entry[$i]; - $lineTypeData = $this->getLineKeyValue($line); - if ($lineTypeData[Importer::KEY] != Importer::CHANGETYPE) { - throw new LAMException(_('Invalid data'), htmlspecialchars($line)); - } } - $currentLines[] = $line; + else { + $currentLines[] = $line; + } } - $subtasks[] = $this->getChangeTypeTask($dn, $currentLines, $type); + $subtasks[] = $this->getChangeTypeTask($dn, $currentLines); return new MultiTask($subtasks, $dn); } } @@ -326,7 +328,7 @@ class Importer { $newRdn = $newRdnData[Importer::VALUE]; $delOldRdnData = $this->getLineKeyValue($entry[1]); if (($delOldRdnData[Importer::KEY] !== 'deleteoldrdn') || !in_array($delOldRdnData[Importer::VALUE], array('0', '1'), true)) { - throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . '
' . $delOldRdnData); + throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . '
' . $entry[1]); } $delOldRdn = ($delOldRdnData[Importer::VALUE] === '0') ? false : true; return new RenameEntryTask($dn, $newRdn, $delOldRdn); @@ -337,19 +339,31 @@ class Importer { * * @param string $dn DN * @param string $lines lines - * @param string $type change type * @return ImporterTask task */ - private function getChangeTypeTask($dn, $lines, $type) { + private function getChangeTypeTask($dn, $lines) { + $firstLine = array_shift($lines); + $firstLineData = $this->getLineKeyValue($firstLine); + $type = $firstLineData[Importer::KEY]; + $attributeName = $firstLineData[Importer::VALUE]; $attributes = array(); foreach ($lines as $line) { $lineData = $this->getLineKeyValue($line); - $attributes[$lineData[Importer::KEY]][] = $lineData[Importer::VALUE]; + if ($lineData[Importer::KEY] !== $attributeName) { + throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . ' - ' . htmlspecialchars($type)); + } + $attributes[$attributeName][] = $lineData[Importer::VALUE]; } if ($type === 'add') { - return new AddEntryTask($dn, $attributes); + return new AddAttributesTask($dn, $attributes); } - throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . ' - ' . Importer::CHANGETYPE . ': ' . htmlspecialchars($type)); + elseif ($type === 'delete') { + return new DeleteAttributesTask($dn, $attributeName, $attributes); + } + elseif ($type === 'replace') { + return new ReplaceAttributesTask($dn, $attributes); + } + throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . ' - ' . htmlspecialchars($type)); } /** @@ -599,6 +613,158 @@ class AddAttributesTask implements ImporterTask { throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap)); } + /** + * Returns the DN. + * + * @return string DN + */ + public function getDn() { + return $this->dn; + } + + /** + * Returns the attributes to add. + * + * @return string[] attributes (array('attr' => array('val1', 'val2'))) + */ + public function getAttributes() { + return $this->attributes; + } + +} + +/** + * Deletes attributes from an existing LDAP entry. + * + * @author Roland Gruber + */ +class DeleteAttributesTask implements ImporterTask { + + private $dn = ''; + private $attributes = array(); + private $attributeName = null; + + /** + * Constructor + * + * @param string $dn DN + * @param string $attributeName attribute name + * @param array[string[]] $attributes list of attributes + */ + public function __construct($dn, $attributeName, $attributes) { + $this->dn = $dn; + $this->attributeName = $attributeName; + $this->attributes = $attributes; + } + + /** + * {@inheritDoc} + * @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run() + */ + public function run() { + $ldap = $_SESSION['ldap']->server(); + if (!empty($this->attributes)) { + $success = @ldap_mod_del($ldap, $this->dn, $this->attributes); + } + else { + $success = @ldap_modify($ldap, $this->dn, array($this->attributeName => array())); + } + if ($success) { + return ''; + } + throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap)); + } + + /** + * Returns the DN. + * + * @return string DN + */ + public function getDn() { + return $this->dn; + } + + /** + * Returns the attributes to add. + * + * @return string[] attributes (array('attr' => array('val1', 'val2'))) + */ + public function getAttributes() { + return $this->attributes; + } + + /** + * Returns the attributes name. + * + * @return string name + */ + public function getAttributeName() { + return $this->attributeName; + } + +} + +/** + * Replaces attributes in an existing LDAP entry. + * + * @author Roland Gruber + */ +class ReplaceAttributesTask implements ImporterTask { + + private $dn = ''; + private $attributes = array(); + + /** + * Constructor + * + * @param string $dn DN + * @param array[string[]] $attributes list of attributes + */ + public function __construct($dn, $attributes) { + $this->dn = $dn; + $this->attributes = $attributes; + } + + /** + * {@inheritDoc} + * @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run() + */ + public function run() { + $ldap = $_SESSION['ldap']->server(); + $success = @ldap_modify($ldap, $this->dn, $this->attributes); + if ($success) { + return ''; + } + throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap)); + } + + /** + * Returns the DN. + * + * @return string DN + */ + public function getDn() { + return $this->dn; + } + + /** + * Returns the attributes to add. + * + * @return string[] attributes (array('attr' => array('val1', 'val2'))) + */ + public function getAttributes() { + return $this->attributes; + } + + /** + * Returns the attributes name. + * + * @return string name + */ + public function getAttributeName() { + return $this->attributeName; + } + } ?> diff --git a/lam/tests/lib/importTest.php b/lam/tests/lib/importTest.php index 9cfbcff9..991ce0ad 100644 --- a/lam/tests/lib/importTest.php +++ b/lam/tests/lib/importTest.php @@ -5,6 +5,8 @@ use LAM\TOOLS\IMPORT_EXPORT\AddAttributesTask; use LAM\TOOLS\IMPORT_EXPORT\AddEntryTask; use LAM\TOOLS\IMPORT_EXPORT\RenameEntryTask; use LAM\TOOLS\IMPORT_EXPORT\DeleteEntryTask; +use LAM\TOOLS\IMPORT_EXPORT\DeleteAttributesTask; +use LAM\TOOLS\IMPORT_EXPORT\ReplaceAttributesTask; /* This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) @@ -257,4 +259,184 @@ class ImporterTest extends PHPUnit_Framework_TestCase { $this->assertEquals(DeleteEntryTask::class, get_class($task)); } + /** + * Change entry with modify changetype with invalid operation. + */ + public function testChangeModifyInvalid() { + $lines = array( + "version: 1", + "", + "dn: uid=test,dc=example,dc=com", + "changeType: modify", + "invalid: test", + ); + + $this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com'); + + $importer = new Importer(); + $tasks = $importer->getTasks($lines); + } + + /** + * Change entry with modify changetype and add operation. + */ + public function testChangeModifyAdd() { + $lines = array( + "version: 1", + "", + "dn: uid=test,dc=example,dc=com", + "changeType: modify", + "add: uid", + "uid: uid1", + "uid: uid2" + ); + + $importer = new Importer(); + $tasks = $importer->getTasks($lines); + $this->assertEquals(1, sizeof($tasks)); + $task = $tasks[0]; + $this->assertEquals(MultiTask::class, get_class($task)); + $subtasks = $task->getTasks(); + $this->assertEquals(1, sizeof($subtasks)); + $subTask = $subtasks[0]; + $this->assertEquals(AddAttributesTask::class, get_class($subTask)); + $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com'); + $attributes = $subTask->getAttributes(); + $this->assertEquals(1, sizeof($attributes)); + $this->assertEquals(2, sizeof($attributes['uid'])); + $this->assertTrue(in_array('uid1', $attributes['uid'])); + $this->assertTrue(in_array('uid2', $attributes['uid'])); + } + + /** + * Change entry with modify changetype and two add operations. + */ + public function testChangeModifyAddTwice() { + $lines = array( + "version: 1", + "", + "dn: uid=test,dc=example,dc=com", + "changeType: modify", + "add: uid", + "uid: uid1", + "uid: uid2", + "-", + "add: gn", + "gn: name1", + "gn: name2" + ); + + $importer = new Importer(); + $tasks = $importer->getTasks($lines); + $this->assertEquals(1, sizeof($tasks)); + $task = $tasks[0]; + $this->assertEquals(MultiTask::class, get_class($task)); + $subtasks = $task->getTasks(); + $this->assertEquals(2, sizeof($subtasks)); + $subTask = $subtasks[0]; + $this->assertEquals(AddAttributesTask::class, get_class($subTask)); + $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com'); + $attributes = $subTask->getAttributes(); + $this->assertEquals(1, sizeof($attributes)); + $this->assertEquals(2, sizeof($attributes['uid'])); + $this->assertTrue(in_array('uid1', $attributes['uid'])); + $this->assertTrue(in_array('uid2', $attributes['uid'])); + $subTask = $subtasks[1]; + $this->assertEquals(AddAttributesTask::class, get_class($subTask)); + $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com'); + $attributes = $subTask->getAttributes(); + $this->assertEquals(1, sizeof($attributes)); + $this->assertEquals(2, sizeof($attributes['gn'])); + $this->assertTrue(in_array('name1', $attributes['gn'])); + $this->assertTrue(in_array('name2', $attributes['gn'])); + } + + /** + * Change entry with modify changetype and delete operation. + */ + public function testChangeModifyDelete() { + $lines = array( + "version: 1", + "", + "dn: uid=test,dc=example,dc=com", + "changeType: modify", + "delete: uid", + "uid: uid1", + "uid: uid2" + ); + + $importer = new Importer(); + $tasks = $importer->getTasks($lines); + $this->assertEquals(1, sizeof($tasks)); + $task = $tasks[0]; + $this->assertEquals(MultiTask::class, get_class($task)); + $subtasks = $task->getTasks(); + $this->assertEquals(1, sizeof($subtasks)); + $subTask = $subtasks[0]; + $this->assertEquals(DeleteAttributesTask::class, get_class($subTask)); + $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com'); + $attributes = $subTask->getAttributes(); + $this->assertEquals(1, sizeof($attributes)); + $this->assertEquals(2, sizeof($attributes['uid'])); + $this->assertTrue(in_array('uid1', $attributes['uid'])); + $this->assertTrue(in_array('uid2', $attributes['uid'])); + } + + /** + * Change entry with modify changetype and delete operation. + */ + public function testChangeModifyDeleteAll() { + $lines = array( + "version: 1", + "", + "dn: uid=test,dc=example,dc=com", + "changeType: modify", + "delete: uid", + ); + + $importer = new Importer(); + $tasks = $importer->getTasks($lines); + $this->assertEquals(1, sizeof($tasks)); + $task = $tasks[0]; + $this->assertEquals(MultiTask::class, get_class($task)); + $subtasks = $task->getTasks(); + $this->assertEquals(1, sizeof($subtasks)); + $subTask = $subtasks[0]; + $this->assertEquals(DeleteAttributesTask::class, get_class($subTask)); + $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com'); + $attributes = $subTask->getAttributes(); + $this->assertTrue(empty($attributes)); + } + + /** + * Change entry with modify changetype and replace operation. + */ + public function testChangeModifyReplace() { + $lines = array( + "version: 1", + "", + "dn: uid=test,dc=example,dc=com", + "changeType: modify", + "replace: uid", + "uid: uid1", + "uid: uid2", + ); + + $importer = new Importer(); + $tasks = $importer->getTasks($lines); + $this->assertEquals(1, sizeof($tasks)); + $task = $tasks[0]; + $this->assertEquals(MultiTask::class, get_class($task)); + $subtasks = $task->getTasks(); + $this->assertEquals(1, sizeof($subtasks)); + $subTask = $subtasks[0]; + $this->assertEquals(ReplaceAttributesTask::class, get_class($subTask)); + $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com'); + $attributes = $subTask->getAttributes(); + $this->assertEquals(1, sizeof($attributes)); + $this->assertEquals(2, sizeof($attributes['uid'])); + $this->assertTrue(in_array('uid1', $attributes['uid'])); + $this->assertTrue(in_array('uid2', $attributes['uid'])); + } + }