Page MenuHomePhorge

D5268.1775305591.diff
No OneTemporary

Authored By
Unknown
Size
9 KB
Referenced Files
None
Subscribers
None

D5268.1775305591.diff

diff --git a/src/app/Console/Commands/Data/Import/LdifCommand.php b/src/app/Console/Commands/Data/Import/LdifCommand.php
--- a/src/app/Console/Commands/Data/Import/LdifCommand.php
+++ b/src/app/Console/Commands/Data/Import/LdifCommand.php
@@ -106,6 +106,7 @@
$this->importSharedFolders();
$this->importResources();
$this->importGroups();
+ $this->importContacts();
// Print warnings collected in the whole process
$this->printWarnings();
@@ -205,6 +206,38 @@
$this->info("DONE");
}
+ /**
+ * Import contacts from the temp table
+ */
+ protected function importContacts(): void
+ {
+ $contacts = DB::table(self::$table)->where('type', 'contact')->whereNull('error')->get();
+
+ $bar = $this->createProgressBar(count($contacts), "Importing contacts");
+
+ foreach ($contacts as $_contact) {
+ $bar->advance();
+
+ $data = json_decode($_contact->data);
+
+ $contact = $this->wallet->owner->contacts()->where('email', $data->email)->first();
+
+ if ($contact) {
+ $this->setImportWarning($_contact->id, "Contact already exists");
+ continue;
+ }
+
+ $this->wallet->owner->contacts()->create([
+ 'name' => $data->name,
+ 'email' => $data->email,
+ ]);
+ }
+
+ $bar->finish();
+
+ $this->info("DONE");
+ }
+
/**
* Import domains from the temp table
*/
@@ -579,14 +612,19 @@
*
* @param array $entry LDAP entry attributes
*
- * @return array Record data for inserting to the temp table
+ * @return array|null Record data for inserting to the temp table
*/
- protected function parseLDAPEntry(array $entry): array
+ protected function parseLDAPEntry(array $entry): ?array
{
$type = null;
$data = null;
$error = null;
+ $classTypeMap = [
+ 'domain' => 'domain',
+ 'user' => 'kolabinetorgperson',
+ ];
+
$ouTypeMap = [
'Shared Folders' => 'sharedfolder',
'Resources' => 'resource',
@@ -595,10 +633,49 @@
'Domains' => 'domain',
];
- foreach ($ouTypeMap as $ou => $_type) {
- if (stripos($entry['dn'], ",ou={$ou}")) {
- $type = $_type;
- break;
+ // Ignore LDIF header
+ if (!empty($entry['version'])) {
+ return null;
+ }
+
+ if (!isset($entry['objectclass'])) {
+ $entry['objectclass'] = [];
+ }
+
+ // Skip non-importable entries
+ if (
+ preg_match('/uid=(cyrus-admin|kolab-service)/', $entry['dn'])
+ || in_array('nsroledefinition', $entry['objectclass'])
+ || in_array('organizationalUnit', $entry['objectclass'])
+ || in_array('organizationalunit', $entry['objectclass'])
+ ) {
+ return null;
+ }
+
+ // Special handling for contacts
+ if (empty($entry['userpassword']) && empty($entry['kolabtargetfolder']) && empty($entry['owner'])
+ && !empty($entry['mail']) && in_array('mailrecipient', $entry['objectclass'])
+ ) {
+ $type = 'contact';
+ }
+
+ // Derive object type from objectclass attribute
+ if (empty($type) && !empty($entry['objectclass'])) {
+ foreach ($classTypeMap as $_type => $class) {
+ if (in_array($class, $entry['objectclass'])) {
+ $type = $_type;
+ break;
+ }
+ }
+ }
+
+ // Drive object type from DN
+ if (empty($type)) {
+ foreach ($ouTypeMap as $ou => $_type) {
+ if (stripos($entry['dn'], ",ou={$ou}")) {
+ $type = $_type;
+ break;
+ }
}
}
@@ -606,6 +683,11 @@
$error = "Unknown record type";
}
+ // Silently ignore groups with no 'mail' attribute
+ if ($type == 'group' && empty($entry['mail'])) {
+ return null;
+ }
+
if (empty($error)) {
$method = 'parseLDAP' . ucfirst($type);
[$data, $error] = $this->{$method}($entry);
@@ -623,6 +705,27 @@
];
}
+ /**
+ * Convert LDAP GAL entry into Kolab4 "format"
+ */
+ protected function parseLDAPContact($entry)
+ {
+ $error = null;
+ $result = [];
+
+ if (empty($entry['mail'])) {
+ $error = "Missing 'mail' attribute";
+ } else {
+ if (!empty($entry['cn'])) {
+ $result['name'] = $this->attrStringValue($entry, 'cn');
+ }
+
+ $result['email'] = strtolower($this->attrStringValue($entry, 'mail'));
+ }
+
+ return [$result, $error];
+ }
+
/**
* Convert LDAP domain data into Kolab4 "format"
*/
@@ -631,18 +734,19 @@
$error = null;
$result = [];
- if (empty($entry['associateddomain'])) {
- $error = "Missing 'associatedDomain' attribute";
- } elseif (!empty($entry['inetdomainstatus']) && $entry['inetdomainstatus'] == 'deleted') {
+ if (!empty($entry['inetdomainstatus']) && $entry['inetdomainstatus'] == 'deleted') {
$error = "Domain deleted";
- } else {
+ } elseif (!empty($entry['associateddomain'])) {
+ // TODO: inetdomainstatus = suspended ???
$result['namespace'] = strtolower($this->attrStringValue($entry, 'associateddomain'));
if (is_array($entry['associateddomain']) && count($entry['associateddomain']) > 1) {
$result['aliases'] = array_slice($entry['associateddomain'], 1);
}
-
- // TODO: inetdomainstatus = suspended ???
+ } elseif (!empty($entry['dn']) && str_starts_with($entry['dn'], 'dc=')) {
+ $result['namespace'] = strtolower(str_replace(['dc=', ','], ['', '.'], $entry['dn']));
+ } else {
+ $error = "Missing 'associatedDomain' and 'dn' attribute";
}
return [$result, $error];
diff --git a/src/app/Utils.php b/src/app/Utils.php
--- a/src/app/Utils.php
+++ b/src/app/Utils.php
@@ -55,7 +55,12 @@
public static function countryForIP($ip, $fallback = 'CH')
{
if (!str_contains($ip, ':')) {
- $net = IP4Net::getNet($ip);
+ // Skip the query if private network
+ if (str_starts_with($ip, '127.')) {
+ $net = null;
+ } else {
+ $net = IP4Net::getNet($ip);
+ }
} else {
$net = IP6Net::getNet($ip);
}
diff --git a/src/tests/Feature/Console/Data/Import/LdifTest.php b/src/tests/Feature/Console/Data/Import/LdifTest.php
--- a/src/tests/Feature/Console/Data/Import/LdifTest.php
+++ b/src/tests/Feature/Console/Data/Import/LdifTest.php
@@ -39,10 +39,6 @@
$this->assertStringNotContainsString("Importing", $output);
$this->assertStringNotContainsString("WARNING", $output);
- $this->assertStringContainsString(
- "ERROR cn=error,ou=groups,ou=kolab3.com,dc=hosted,dc=com: Missing 'mail' attribute",
- $output
- );
$this->assertStringContainsString(
"ERROR cn=error,ou=resources,ou=kolab3.com,dc=hosted,dc=com: Missing 'mail' attribute",
$output
@@ -166,6 +162,13 @@
$this->assertMatchesRegularExpression('/^resource-[0-9]+@kolab3\.com$/', $resources[0]->email);
$this->assertSame('shared/Resource@kolab3.com', $resources[0]->getSetting('folder'));
$this->assertSame('manual:user@kolab3.com', $resources[0]->getSetting('invitation_policy'));
+
+ // Contacts
+ $contacts = $owner->contacts()->orderBy('email')->get();
+
+ $this->assertCount(1, $contacts);
+ $this->assertSame('contact1@internal.domain.tld', $contacts[0]->email);
+ $this->assertSame('Contact1', $contacts[0]->name);
}
/**
@@ -249,6 +252,24 @@
$this->assertSame(['test', 'test3', '-'], $result);
}
+ /**
+ * Test parseLDAPContact() method
+ */
+ public function testParseLDAPContact(): void
+ {
+ $command = new LdifCommand();
+
+ $entry = [];
+ $result = $this->invokeMethod($command, 'parseLDAPContact', [$entry]);
+ $this->assertSame([], $result[0]);
+ $this->assertSame("Missing 'mail' attribute", $result[1]);
+
+ $entry = ['mail' => ['test@test.com'], 'cn' => 'Test'];
+ $result = $this->invokeMethod($command, 'parseLDAPContact', [$entry]);
+ $this->assertSame(['name' => 'Test', 'email' => 'test@test.com'], $result[0]);
+ $this->assertNull($result[1]);
+ }
+
/**
* Test parseLDAPDomain() method
*/
@@ -259,7 +280,7 @@
$entry = [];
$result = $this->invokeMethod($command, 'parseLDAPDomain', [$entry]);
$this->assertSame([], $result[0]);
- $this->assertSame("Missing 'associatedDomain' attribute", $result[1]);
+ $this->assertSame("Missing 'associatedDomain' and 'dn' attribute", $result[1]);
$entry = ['associateddomain' => 'test.com'];
$result = $this->invokeMethod($command, 'parseLDAPDomain', [$entry]);
diff --git a/src/tests/data/kolab3.ldif b/src/tests/data/kolab3.ldif
--- a/src/tests/data/kolab3.ldif
+++ b/src/tests/data/kolab3.ldif
@@ -119,3 +119,11 @@
kolabTargetFolder: shared/Folder2@kolab3.com
mail: folder2@kolab3.com
acl: anyone, read-only
+
+# sample GAL entry
+dn: CN=Contact1,OU=Organizations,ou=Groups,dc=kolab3.com,dc=hosted,dc=com
+objectClass: groupofuniquenames
+objectClass: mailrecipient
+objectClass: top
+cn: Contact1
+mail: contact1@internal.domain.tld

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 4, 12:26 PM (12 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18829425
Default Alt Text
D5268.1775305591.diff (9 KB)

Event Timeline