Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117835358
D5268.1775305591.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
9 KB
Referenced Files
None
Subscribers
None
D5268.1775305591.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D5268: data:import:ldif command improvements
Attached
Detach File
Event Timeline