diff --git a/.env.testing b/.env.testing index ef55ac0f..313f8c9f 100644 --- a/.env.testing +++ b/.env.testing @@ -3,7 +3,7 @@ APP_ENV=local APP_KEY= APP_DEBUG=true -LOG_CHANNEL=stderr +LOG_CHANNEL=single CACHE_DRIVER=array QUEUE_CONNECTION=sync diff --git a/app/Classes/LDAP/Attribute.php b/app/Classes/LDAP/Attribute.php index 3b144083..f0403bb4 100644 --- a/app/Classes/LDAP/Attribute.php +++ b/app/Classes/LDAP/Attribute.php @@ -190,6 +190,21 @@ class Attribute implements \Countable, \ArrayAccess ->get($tag,[]),$values))); } + /** + * If this attribute has changes, re-render the attribute values + * + * @return array + */ + public function getDirty(): array + { + $dirty = []; + + if ($this->isDirty()) + $dirty = [$this->name_lc => $this->_values->toArray()]; + + return $dirty; + } + /** * Return the hints about this attribute, ie: RDN, Required, etc * diff --git a/app/Classes/LDAP/Import.php b/app/Classes/LDAP/Import.php index 61525508..6dfa783a 100644 --- a/app/Classes/LDAP/Import.php +++ b/app/Classes/LDAP/Import.php @@ -56,6 +56,7 @@ abstract class Import { switch ($action) { case static::LDAP_IMPORT_ADD: + case static::LDAP_IMPORT_MODIFY: try { $o->save(); @@ -65,15 +66,17 @@ abstract class Import if ($e->getDetailedError()) return collect([ 'dn'=>$o->getDN(), - 'result'=>sprintf('%d: %s (%s)', + 'link'=>$o->getDNSecure(), + 'result'=>sprintf('%d: %s%s', ($x=$e->getDetailedError())->getErrorCode(), $x->getErrorMessage(), - $x->getDiagnosticMessage(), + $x->getDiagnosticMessage() ? ' ('.$x->getDiagnosticMessage().')' : '', ) ]); else return collect([ 'dn'=>$o->getDN(), + 'link'=>$o->getDNSecure(), 'result'=>sprintf('%d: %s', $e->getCode(), $e->getMessage(), @@ -81,9 +84,13 @@ abstract class Import ]); } - Log::debug(sprintf('%s:Import Commited',self::LOGKEY)); + Log::debug(sprintf('%s:= Import Commited',self::LOGKEY)); - return collect(['dn'=>$o->getDN(),'result'=>__('Created')]); + return collect([ + 'dn'=>$o->getDN(), + 'link'=>$o->getDNSecure(), + 'result'=>$action === self::LDAP_IMPORT_ADD ? __('Created') : __('Modified'), + ]); default: throw new GeneralException('Unhandled action during commit: '.$action); diff --git a/app/Classes/LDAP/Import/LDIF.php b/app/Classes/LDAP/Import/LDIF.php index fc4e5896..d5e7b7ae 100644 --- a/app/Classes/LDAP/Import/LDIF.php +++ b/app/Classes/LDAP/Import/LDIF.php @@ -24,7 +24,9 @@ class LDIF extends Import public function process(): Collection { $c = 0; - $action = NULL; + $action = self::LDAP_IMPORT_ADD; // Assume add mode + $subaction = 'add'; // Assume add + $dn = NULL; $attribute = NULL; $base64encoded = FALSE; $o = NULL; @@ -32,23 +34,38 @@ class LDIF extends Import $version = NULL; $result = collect(); - // @todo When renaming DNs, the hotlink should point to the new entry on success, or the old entry on failure. foreach (preg_split('/(\r?\n|\r)/',$this->input) as $line) { $c++; - Log::debug(sprintf('%s:LDIF Line [%s]',self::LOGKEY,$line)); + Log::debug(sprintf('%s:LDIF Line [%s] (%d)',self::LOGKEY,$line,$c)); $line = trim($line); // If the line starts with a comment, ignore it if (preg_match('/^#/',$line)) continue; + // If the line starts with a dash, its more updates to the same entry + if (preg_match('/^-/',$line)) { + Log::debug(sprintf('%s:/ DASH Line [%s] (%d)',self::LOGKEY,$line,$c),['action'=>$action,'subaction'=>$subaction,'attribute'=>$attribute,'value'=>$value]); + if ($attribute) + $o = $this->entry($o,$attribute,$subaction,$base64encoded ? base64_decode($value) : $value,$c); + + $base64encoded = FALSE; + $attribute = NULL; + $value = ''; + continue; + } + // If we have a blank line, then that completes this command if (! $line) { - if (! is_null($o)) { - // Add the last attribute; - $o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value); + // If we havent got a version yet, then we havent started + if (! $version) { + continue; - Log::debug(sprintf('%s:- Committing Entry [%s]',self::LOGKEY,$o->getDN())); + } elseif (! is_null($o)) { + if ($attribute) + $o = $this->entry($o,$attribute,$subaction,$base64encoded ? base64_decode($value) : $value,$c); + + Log::debug(sprintf('%s:- Committing Entry (More) [%s]',self::LOGKEY,$o->getDN())); // Commit $result->push($this->commit($o,$action)); @@ -59,27 +76,123 @@ class LDIF extends Import $base64encoded = FALSE; $attribute = NULL; $value = ''; + + } else { + throw new GeneralException(sprintf('Processing Error - Line exists [%s] on (%d) but object is NULL',$line,$c)); } continue; } $m = []; - preg_match('/^([a-zA-Z0-9;-]+)(:+)\s+(.*)$/',$line,$m); + preg_match('/^([a-zA-Z0-9;-]+)(:+)\s*(.*)$/',$line,$m); + + // If $m is NULL, then this is the 2nd (or more) line of a base64 encoded value + if (! $m) { + $value .= $line; + Log::debug(sprintf('%s:+ attribute [%s] appending [%s] (%d)',self::LOGKEY,$attribute,$line,$c)); + + // add to last attr value + continue; + + } else { + // If base64 mode was enabled, and there is a following attribute after a base64encoded attribute, it hasnt been processed yet + if ($base64encoded) { + Log::debug(sprintf('%s:- Completing base64 attribute [%s]',self::LOGKEY,$attribute)); + + $o = $this->entry($o,$attribute,$subaction,base64_decode($value),$c); + + $attribute = NULL; + } + + $base64encoded = ($m[2] === '::'); + $value = $m[3]; + + // If we are base64encoded, we need to loop around + if ($base64encoded) { + $attribute = $m[1]; + Log::debug(sprintf('%s:/ Retrieving base64 attribute [%s] (%c)',self::LOGKEY,$attribute,$c)); + + continue; + } + } + + // changetype needs to be after the dn, and if it isnt we'll assume add. + if ($dn && Arr::get($m,1) !== 'changetype') { + if ($action === self::LDAP_IMPORT_ADD) { + Log::debug(sprintf('%s:Creating new entry [%s]:',self::LOGKEY,$dn),['o'=>$o]); + $o = new Entry; + $o->setDn($dn); + $dn = NULL; + + } else { + Log::debug(sprintf('%s:Looking for existing entry [%s]:',self::LOGKEY,$dn),['o'=>$o]); + $o = Entry::find($dn); + $dn = NULL; + + if (! $o) { + $result->push(collect(['dn'=>$dn,'result'=>__('Entry doesnt exist')])); + $result->last()->put('line',$c);; + + continue; + } + } + } switch (Arr::get($m,1)) { + case 'dn': + $dn = $base64encoded ? base64_decode($value) : $value; + Log::debug(sprintf('%s:# Got DN [%s]:',self::LOGKEY,$dn)); + + $value = ''; + $base64encoded = FALSE; + break; + case 'changetype': if ($m[2] !== ':') - throw new GeneralException(sprintf('ChangeType cannot be base64 encoded set at [%d]. (line %d)',$version,$c)); + throw new GeneralException(sprintf('changetype cannot be base64 encoded set at [%d]. (line %d)',$version,$c)); + + if (! is_null($o)) + throw new GeneralException(sprintf('Previous Entry not complete? (line %d)',$c)); + + Log::debug(sprintf('%s:- Action [%s]',self::LOGKEY,$m[3])); switch ($m[3]) { - // if (preg_match('/^changetype:[ ]*(delete|add|modrdn|moddn|modify)/i',$lines[0])) { + case 'add': + $action = self::LDAP_IMPORT_ADD; + break; + + case 'modify': + $action = self::LDAP_IMPORT_MODIFY; + break; + + /* + case 'delete': + $action = self::LDAP_IMPORT_DELETE; + break; + */ + + // @todo modrdn|moddn default: throw new NotImplementedException(sprintf('Unknown change type [%s]? (line %d)',$m[3],$c)); } break; + case 'add': + case 'replace': + if ($action !== self::LDAP_IMPORT_MODIFY) + throw new GeneralException(sprintf('%s action can only be used with changetype: modify (line %d)',$m[1],$c)); + + $subaction = $m[1]; + break; + + case 'delete': + $subaction = $m[1]; + $attribute = $m[3]; + $value = NULL; + break; + case 'version': if (! is_null($version)) throw new VersionException(sprintf('Version has already been set at [%d]. (line %d)',$version,$c)); @@ -92,49 +205,14 @@ class LDIF extends Import // Treat it as an attribute default: - // If $m is NULL, then this is the 2nd (or more) line of a base64 encoded value - if (! $m) { - $value .= $line; - Log::debug(sprintf('%s:- Attribute [%s] adding [%s] (%d)',self::LOGKEY,$attribute,$line,$c)); - - // add to last attr value - continue 2; - } + // Start of a new attribute + $attribute = $m[1]; + Log::debug(sprintf('%s:- Working with Attribute [%s] with [%s] (%d)',self::LOGKEY,$attribute,$value,$c)); // We are ready to create the entry or add the attribute - if ($attribute) { - if ($attribute === 'dn') { - if (! is_null($o)) - throw new GeneralException(sprintf('Previous Entry not complete? (line %d)',$c)); - - $dn = $base64encoded ? base64_decode($value) : $value; - Log::debug(sprintf('%s:Creating new entry:',self::LOGKEY,$dn)); - //$o = Entry::find($dn); - - // If it doesnt exist, we'll create it - //if (! $o) { - $o = new Entry; - $o->setDn($dn); - //} - - $action = self::LDAP_IMPORT_ADD; - - } else { - Log::debug(sprintf('%s:Adding Attribute [%s] value [%s] (%d)',self::LOGKEY,$attribute,$value,$c)); - - if ($value) - $o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value); - else - throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c)); - } - } - - // Start of a new attribute - $base64encoded = ($m[2] === '::'); - $attribute = $m[1]; - $value = $m[3]; - - Log::debug(sprintf('%s:- New Attribute [%s] with [%s] (%d)',self::LOGKEY,$attribute,$value,$c)); + $o = $this->entry($o,$attribute,$subaction,$base64encoded ? base64_decode($value) : $value,$c); + $attribute = NULL; + $value = NULL; } if ($version !== 1) @@ -142,11 +220,11 @@ class LDIF extends Import } // We may still have a pending action - if ($action) { - // Add the last attribute; - $o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value); + if ($o) { + if ($attribute) + $o = $this->entry($o,$attribute,$subaction,$base64encoded ? base64_decode($value) : $value,$c); - Log::debug(sprintf('%s:- Committing Entry [%s]',self::LOGKEY,$o->getDN())); + Log::debug(sprintf('%s:- Committing Entry (Final) [%s]',self::LOGKEY,$o->getDN())); // Commit $result->push($this->commit($o,$action)); @@ -156,75 +234,46 @@ class LDIF extends Import return $result; } - public function xreadEntry() { - static $haveVersion = FALSE; + private function entry(Entry $o,string $attribute,string $subaction,?string $value,int $c): Entry + { + Log::debug(sprintf('%s:. %s Attribute [%s] value [%s] (%d)',self::LOGKEY,$subaction,$attribute,$value,$c)); - if ($lines = $this->nextLines()) { + switch ($subaction) { + case 'add': + if (! $value) + throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c)); - $server = $this->getServer(); + $o->addAttributeItem($attribute,$value); + break; - # The first line should be the DN - if (preg_match('/^dn:/',$lines[0])) { - list($text,$dn) = $this->getAttrValue(array_shift($lines)); + case 'replace': + if (! $value) + throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c)); - # The second line should be our changetype - if (preg_match('/^changetype:[ ]*(delete|add|modrdn|moddn|modify)/i',$lines[0])) { - $attrvalue = $this->getAttrValue($lines[0]); - $changetype = $attrvalue[1]; - array_shift($lines); + if (! ($x=$o->getObject(($xx=strstr($attribute,';',TRUE)) ?: $attribute))) + throw new \Exception(sprintf('Attribute [%s] doesnt exist in [%s] (line %d)',$attribute,$o->getDn(),$c)); + + // If the attribute has changed, we'll assume this is an additional value for it + if ($x->isDirty()) { + Log::debug(sprintf('%s:/ Attribute [%s] has changed, assuming add',self::LOGKEY,$attribute)); + $o->addAttributeItem($attribute,$value); } else - $changetype = 'add'; + $o->{$attribute} = [Entry::TAG_NOTAG=>[$value]]; - $this->template = new Template($this->server_id,NULL,NULL,$changetype); + break; - switch ($changetype) { - case 'add': - $rdn = get_rdn($dn); - $container = $server->getContainer($dn); + case 'delete': + if (! $o->getObject($attribute)) + throw new \Exception(sprintf('Attribute [%s] doesnt exist in [%s] (line %d)',$attribute,$o->getDn(),$c)); - $this->template->setContainer($container); - $this->template->accept(); + $o->{$attribute} = []; + break; - $this->getAddDetails($lines); - $this->template->setRDNAttributes($rdn); + default: + throw new \Exception('Unknown subaction:'.$subaction); + } - return $this->template; - - break; - - case 'modify': - if (! $server->dnExists($dn)) - return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines); - - $this->template->setDN($dn); - $this->template->accept(FALSE,TRUE); - - return $this->getModifyDetails($lines); - - break; - - case 'moddn': - case 'modrdn': - if (! $server->dnExists($dn)) - return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines); - - $this->template->setDN($dn); - $this->template->accept(); - - return $this->getModRDNAttributes($lines); - - break; - - default: - if (! $server->dnExists($dn)) - return $this->error(_('Unknown change type'),$lines); - } - - } else - return $this->error(_('A valid dn line is required'),$lines); - - } else - return FALSE; + return $o; } } \ No newline at end of file diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 36796ed8..19111910 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -538,9 +538,13 @@ class HomeController extends Controller $result = $import->process(); } catch (NotImplementedException $e) { + Log::error(sprintf('Import Exception [%s]',$e->getMessage())); + abort(555,$e->getMessage()); } catch (\Exception $e) { + Log::error(sprintf('Import Exception [%s]',$e->getMessage())); + abort(598,$e->getMessage()); } diff --git a/app/Ldap/Entry.php b/app/Ldap/Entry.php index d266d549..084b4f1e 100644 --- a/app/Ldap/Entry.php +++ b/app/Ldap/Entry.php @@ -101,6 +101,30 @@ class Entry extends Model ->toArray(); } + /** + * This replaces the model's get dirty, given that we store LDAP attributes in $this->objects, replacing + * $this->original/$this->attributes + * + * @return array + */ + public function getDirty(): array + { + $result = collect(); + + foreach ($this->objects as $o) + if ($o->isDirty()) + $result = $result->merge($o->getDirty()); + + $result = $result + ->flatMap(function($item,$attr) { + return ($x=collect($item))->count() + ? $x->flatMap(fn($v,$k)=>[strtolower($attr.(($k !== self::TAG_NOTAG) ? ';'.$k : ''))=>$v]) + : [strtolower($attr)=>[]]; + }); + + return $result->toArray(); + } + /** * Determine if the new and old values for a given key are equivalent. */ @@ -454,7 +478,7 @@ class Entry extends Model $ot = $this->getOtherTags(); $cache[$tag ?: '_all_'] = $this->objects - ->filter(fn($item)=>(! $item->is_internal) && ((! $item->no_attr_tags) || (! $tag) || ($tag === Entry::TAG_NOTAG))) + ->filter(fn($item)=>(! $item->is_internal) && ((! $item->no_attr_tags) || (! $tag) || ($tag === self::TAG_NOTAG))) ->filter(fn($item)=>(! $tag) || $ot->has($item->name_lc) || count($item->tagValues($tag)) > 0); } diff --git a/resources/views/frames/import_result.blade.php b/resources/views/frames/import_result.blade.php index acd8b04b..cc24baca 100644 --- a/resources/views/frames/import_result.blade.php +++ b/resources/views/frames/import_result.blade.php @@ -30,7 +30,13 @@ @foreach($result as $item) - {{ $item->get('dn') }} + + @if($x=$item->get('link')) + {{ $item->get('dn') }} + @else + {{ $item->get('dn') }} + @endif + {{ $item->get('result') }} {{ $item->get('line') }} diff --git a/tests/Feature/ImportTest.php b/tests/Feature/ImportTest.php index 1c53b7c6..4336b3fb 100644 --- a/tests/Feature/ImportTest.php +++ b/tests/Feature/ImportTest.php @@ -7,8 +7,11 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Crypt; use Tests\TestCase; +use App\Ldap\Entry; + class ImportTest extends TestCase { + // Test delete and create an entry public function testLDIF_Import() { $dn = 'cn=Bart Simpson,ou=People,o=Simpsons'; @@ -27,7 +30,7 @@ class ImportTest extends TestCase $x->delete(); $this->assertEquals(NULL,config('server')->fetch($dn)); - $file = new UploadedFile($import_file,'ldif-import.ldif',null,null,true); + $file = new UploadedFile($import_file,basename($import_file),null,null,true); $response = $this ->actingAs(Auth::user()) @@ -44,5 +47,256 @@ class ImportTest extends TestCase // Check that it hsa been created $this->assertEquals($dn,$x=config('server')->fetch($dn)); $this->assertTrue($x->exists); + $this->assertCount(4,$x->getObject('objectclass')); + $this->assertCount(4,$x->getObject('objectClass')); + $this->assertCount(0,array_diff(['inetOrgPerson','posixAccount','top','shadowAccount'],$x->getObject('objectClass')->values->toArray())); + $this->assertCount(1,$x->getObject('mail')); + $this->assertContains(Entry::TAG_NOTAG.'.0',$x->getObject('mail')->values->dot()->keys()); + $this->assertContains('bart.simpson@example.com',$x->getObject('mail')->values->dot()); + $this->assertEquals(3024,strlen($x->getObject('jpegphoto')->values->dot()->first())); + } + + public function testLDIF_Import_Replace() { + $dn = 'cn=Bart Simpson,ou=People,o=Simpsons'; + $import_file = __DIR__.'/data/ldif-import.1.ldif'; + + $this->assertTrue($this->login()); + + // Check that it exists + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + + $file = new UploadedFile($import_file,basename($import_file),null,null,true); + + $response = $this + ->actingAs(Auth::user()) + ->from('/import') + ->post('/import/process/ldif',[ + '_token' => csrf_token(), + '_key'=>Crypt::encryptString('*import|_NOP'), + 'file' => $file, + ]); + + $response->assertSuccessful(); + + // Check that it hsa been created + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + $this->assertCount(4,$x->getObject('objectclass')); + + $this->assertCount(1,$x->getObject('mail')->values); + $this->assertCount(2,$x->getObject('mail')->tagValues()); + $this->assertCount(0,array_diff(['barts@email.com','secondmail@example.com'],$x->getObject('mail')->values->dot()->values()->toArray())); + + $this->assertCount(1,$x->getObject('facsimiletelephonenumber')->values); + $this->assertCount(1,$x->getObject('facsimiletelephonenumber')->tagValues()); + $this->assertCount(0,array_diff(['+1 555 222 4444'],$x->getObject('facsimiletelephonenumber')->values->dot()->values()->toArray())); + } + + public function testLDIF_Import_Delete() { + $dn = 'cn=Bart Simpson,ou=People,o=Simpsons'; + $import_file = __DIR__.'/data/ldif-import.2.ldif'; + + $this->assertTrue($this->login()); + + // Check that it exists + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + + $file = new UploadedFile($import_file,basename($import_file),null,null,true); + + $response = $this + ->actingAs(Auth::user()) + ->from('/import') + ->post('/import/process/ldif',[ + '_token' => csrf_token(), + '_key'=>Crypt::encryptString('*import|_NOP'), + 'file' => $file, + ]); + + $response->assertSuccessful(); + + // Check that it hsa been created + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + $this->assertCount(4,$x->getObject('objectclass')); + + $this->assertCount(1,$x->getObject('mail')->values); + $this->assertCount(2,$x->getObject('mail')->tagValues()); + $this->assertCount(0,array_diff(['barts@email.com','secondmail@example.com'],$x->getObject('mail')->values->dot()->values()->toArray())); + + $this->assertNull($x->getObject('facsimiletelephonenumber')); + } + + public function testLDIF_Import_Append_Langtag() { + $dn = 'cn=Bart Simpson,ou=People,o=Simpsons'; + $import_file = __DIR__.'/data/ldif-import.3.ldif'; + + $this->assertTrue($this->login()); + + // Check that it exists + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + + $file = new UploadedFile($import_file,basename($import_file),null,null,true); + + $response = $this + ->actingAs(Auth::user()) + ->from('/import') + ->post('/import/process/ldif',[ + '_token' => csrf_token(), + '_key'=>Crypt::encryptString('*import|_NOP'), + 'file' => $file, + ]); + + $response->assertSuccessful(); + + // Check that it hsa been created + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + $this->assertCount(4,$x->getObject('objectclass')); + + $this->assertCount(3,$x->getObject('mail')->values); + $this->assertCount(4,$x->getObject('mail')->values->dot()); + $this->assertCount(2,$x->getObject('mail')->tagValues()); + $this->assertCount(1,$x->getObject('mail')->tagValues('lang-au')); + $this->assertCount(1,$x->getObject('mail')->tagValues('lang-cn')); + $this->assertCount(0,array_diff(['barts@email.com','secondmail@example.com','au-email@example.com','cn-email@example.com'],$x->getObject('mail')->values->dot()->values()->toArray())); + } + + public function testLDIF_Import_Replace_Langtag() { + $dn = 'cn=Bart Simpson,ou=People,o=Simpsons'; + $import_file = __DIR__.'/data/ldif-import.4.ldif'; + + $this->assertTrue($this->login()); + + // Check that it exists + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + + $file = new UploadedFile($import_file,basename($import_file),null,null,true); + + $response = $this + ->actingAs(Auth::user()) + ->from('/import') + ->post('/import/process/ldif',[ + '_token' => csrf_token(), + '_key'=>Crypt::encryptString('*import|_NOP'), + 'file' => $file, + ]); + + $response->assertSuccessful(); + + // Check that it hsa been created + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + $this->assertCount(4,$x->getObject('objectclass')); + + $this->assertCount(3,$x->getObject('mail')->values); + $this->assertCount(4,$x->getObject('mail')->values->dot()); + $this->assertCount(2,$x->getObject('mail')->tagValues()); + $this->assertCount(1,$x->getObject('mail')->tagValues('lang-au')); + $this->assertCount(1,$x->getObject('mail')->tagValues('lang-cn')); + $this->assertCount(0,array_diff(['notag@example.com','notag1@example.com','au-tag@example.com','cn-tag@example.com'],$x->getObject('mail')->values->dot()->values()->toArray())); + } + + public function testLDIF_Import_Add_Base64() + { + $dn = 'cn=Bart Simpson,ou=People,o=Simpsons'; + $import_file = __DIR__.'/data/ldif-import.5.ldif'; + + $this->assertTrue($this->login()); + + // Check that it exists + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + + $file = new UploadedFile($import_file,basename($import_file),null,null,true); + + $response = $this + ->actingAs(Auth::user()) + ->from('/import') + ->post('/import/process/ldif',[ + '_token' => csrf_token(), + '_key'=>Crypt::encryptString('*import|_NOP'), + 'file' => $file, + ]); + + $response->assertSuccessful(); + + // Check that it hsa been created + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + $this->assertCount(4,$x->getObject('objectclass')); + + $this->assertEquals(3396,strlen($x->getObject('jpegphoto')->values->dot()->first())); + } + + public function testLDIF_Import_Replace_Base64() + { + $dn = 'cn=Bart Simpson,ou=People,o=Simpsons'; + $import_file = __DIR__.'/data/ldif-import.6.ldif'; + + $this->assertTrue($this->login()); + + // Check that it exists + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + + $file = new UploadedFile($import_file,basename($import_file),null,null,true); + + $response = $this + ->actingAs(Auth::user()) + ->from('/import') + ->post('/import/process/ldif',[ + '_token' => csrf_token(), + '_key'=>Crypt::encryptString('*import|_NOP'), + 'file' => $file, + ]); + + $response->assertSuccessful(); + + // Check that it hsa been created + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + $this->assertCount(4,$x->getObject('objectclass')); + + $this->assertEquals(3024,strlen($x->getObject('jpegphoto')->values->dot()->first())); + } + + public function testLDIF_Import_Multi() { + $dn = 'cn=Bart Simpson,ou=People,o=Simpsons'; + $import_file = __DIR__.'/data/ldif-import.7.ldif'; + + $this->assertTrue($this->login()); + + // Check that it exists + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + + $file = new UploadedFile($import_file,basename($import_file),null,null,true); + + $response = $this + ->actingAs(Auth::user()) + ->from('/import') + ->post('/import/process/ldif',[ + '_token' => csrf_token(), + '_key'=>Crypt::encryptString('*import|_NOP'), + 'file' => $file, + ]); + + $response->assertSuccessful(); + + // Check that it hsa been created + $this->assertEquals($dn,$x=config('server')->fetch($dn)); + $this->assertTrue($x->exists); + $this->assertCount(4,$x->getObject('objectclass')); + + $this->assertCount(3,$x->getObject('mail')->values); + $this->assertCount(6,$x->getObject('mail')->values->dot()); + $this->assertCount(2,$x->getObject('mail')->tagValues()); + $this->assertCount(2,$x->getObject('mail')->tagValues('lang-au')); + $this->assertCount(2,$x->getObject('mail')->tagValues('lang-cn')); + $this->assertCount(2,array_diff(['notag1@simpsons.example.com','notag2@simpsons.example.com','au-tag@simpsons.example.com','cn-tag@simpsons.example.com'],$x->getObject('mail')->values->dot()->values()->toArray())); } } \ No newline at end of file diff --git a/tests/Feature/data/ldif-import.1.ldif b/tests/Feature/data/ldif-import.1.ldif new file mode 100644 index 00000000..682b7475 --- /dev/null +++ b/tests/Feature/data/ldif-import.1.ldif @@ -0,0 +1,12 @@ +# Multiple actions +version: 1 +dn: cn=Bart Simpson,ou=People,o=Simpsons +changetype: modify +replace: mail +mail: barts@email.com +- +add: mail +mail: secondmail@example.com +- +add: facsimiletelephonenumber +facsimiletelephonenumber: +1 555 222 4444 \ No newline at end of file diff --git a/tests/Feature/data/ldif-import.2.ldif b/tests/Feature/data/ldif-import.2.ldif new file mode 100644 index 00000000..0a8b1267 --- /dev/null +++ b/tests/Feature/data/ldif-import.2.ldif @@ -0,0 +1,5 @@ +# Delete actions +version: 1 +dn: cn=Bart Simpson,ou=People,o=Simpsons +changetype:modify +delete: facsimiletelephonenumber \ No newline at end of file diff --git a/tests/Feature/data/ldif-import.3.ldif b/tests/Feature/data/ldif-import.3.ldif new file mode 100644 index 00000000..152550f4 --- /dev/null +++ b/tests/Feature/data/ldif-import.3.ldif @@ -0,0 +1,7 @@ +# Add lang-tag option with multiple values +version: 1 +dn: cn=Bart Simpson,ou=People,o=Simpsons +changetype: modify +add: mail +mail;lang-au: au-email@example.com +mail;lang-cn: cn-email@example.com \ No newline at end of file diff --git a/tests/Feature/data/ldif-import.4.ldif b/tests/Feature/data/ldif-import.4.ldif new file mode 100644 index 00000000..4f0fd2d1 --- /dev/null +++ b/tests/Feature/data/ldif-import.4.ldif @@ -0,0 +1,9 @@ +# Multiple values with tags and replace +version: 1 +dn: cn=Bart Simpson,ou=People,o=Simpsons +changetype: modify +replace: mail +mail: notag@example.com +mail: notag1@example.com +mail;lang-au: au-tag@example.com +mail;lang-cn: cn-tag@example.com \ No newline at end of file diff --git a/tests/Feature/data/ldif-import.5.ldif b/tests/Feature/data/ldif-import.5.ldif new file mode 100644 index 00000000..59c3f95c --- /dev/null +++ b/tests/Feature/data/ldif-import.5.ldif @@ -0,0 +1,68 @@ +version: 1 +# Entry 1: cn=Bart Simpson,ou=People,o=Simpsons +dn: cn=Bart Simpson,ou=People,o=Simpsons +changetype:modify +delete:jpegphoto +- +add:jpegphoto +jpegPhoto:: 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 + zt1Zj1J/wDtsTcHQxCJtll8Rr3105kOg4DkF//Z diff --git a/tests/Feature/data/ldif-import.6.ldif b/tests/Feature/data/ldif-import.6.ldif new file mode 100644 index 00000000..36260c5b --- /dev/null +++ b/tests/Feature/data/ldif-import.6.ldif @@ -0,0 +1,61 @@ +version: 1 +# Entry 1: cn=Bart Simpson,ou=People,o=Simpsons +dn: cn=Bart Simpson,ou=People,o=Simpsons +changetype:modify +delete:jpegphoto +- +replace:jpegphoto +jpegPhoto:: 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 \ No newline at end of file diff --git a/tests/Feature/data/ldif-import.7.ldif b/tests/Feature/data/ldif-import.7.ldif new file mode 100644 index 00000000..8650d20a --- /dev/null +++ b/tests/Feature/data/ldif-import.7.ldif @@ -0,0 +1,12 @@ +version: 1 +dn: cn=Bart Simpson,ou=People,o=Simpsons +changetype: modify +delete:mail + +dn: cn=Bart Simpson,ou=People,o=Simpsons +changetype: modify +add: mail +mail: notag1@simpsons.example.com +mail: notag2@simpsons.example.com +mail;lang-au: au-email@simpsons.example.com +mail;lang-cn: cn-email@simpsons.example.com \ No newline at end of file