Page MenuHomePhorge

D5268.1775257895.diff
No OneTemporary

Authored By
Unknown
Size
21 KB
Referenced Files
None
Subscribers
None

D5268.1775257895.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/Feature/Controller/AuthTest.php b/src/tests/Feature/Controller/AuthTest.php
--- a/src/tests/Feature/Controller/AuthTest.php
+++ b/src/tests/Feature/Controller/AuthTest.php
@@ -30,7 +30,7 @@
$this->expectedExpiry = \config('auth.token_expiry_minutes') * 60;
- IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
+ IP4Net::where('net_number', inet_pton('128.0.0.0'))->delete();
$user = $this->getTestUser('john@kolab.org');
$user->setSetting('limit_geo', null);
@@ -41,7 +41,7 @@
$this->deleteTestUser('UsersControllerTest1@userscontroller.com');
$this->deleteTestDomain('userscontroller.com');
- IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
+ IP4Net::where('net_number', inet_pton('128.0.0.0'))->delete();
$user = $this->getTestUser('john@kolab.org');
$user->setSetting('limit_geo', null);
@@ -107,19 +107,19 @@
$response = $this->get("api/auth/location");
$response->assertStatus(401);
- $headers = ['X-Client-IP' => '127.0.0.2'];
+ $headers = ['X-Client-IP' => '128.0.0.2'];
$response = $this->actingAs($user)->withHeaders($headers)->get("api/auth/location");
$response->assertStatus(200);
$json = $response->json();
- $this->assertSame('127.0.0.2', $json['ipAddress']);
+ $this->assertSame('128.0.0.2', $json['ipAddress']);
$this->assertSame('', $json['countryCode']);
IP4Net::create([
- 'net_number' => '127.0.0.0',
- 'net_broadcast' => '127.255.255.255',
+ 'net_number' => '128.0.0.0',
+ 'net_broadcast' => '128.255.255.255',
'net_mask' => 8,
'country' => 'US',
'rir_name' => 'test',
@@ -131,7 +131,7 @@
$json = $response->json();
- $this->assertSame('127.0.0.2', $json['ipAddress']);
+ $this->assertSame('128.0.0.2', $json['ipAddress']);
$this->assertSame('US', $json['countryCode']);
}
@@ -233,7 +233,7 @@
$user = $this->getTestUser('john@kolab.org');
$user->setSetting('limit_geo', json_encode(['US']));
- $headers['X-Client-IP'] = '127.0.0.2';
+ $headers['X-Client-IP'] = '128.0.0.2';
$post = ['email' => 'john@kolab.org', 'password' => 'simple123'];
$response = $this->withHeaders($headers)->post("api/auth/login", $post);
@@ -245,8 +245,8 @@
$this->assertSame('error', $json['status']);
IP4Net::create([
- 'net_number' => '127.0.0.0',
- 'net_broadcast' => '127.255.255.255',
+ 'net_number' => '128.0.0.0',
+ 'net_broadcast' => '128.255.255.255',
'net_mask' => 8,
'country' => 'US',
'rir_name' => 'test',
diff --git a/src/tests/Feature/Controller/NGINXTest.php b/src/tests/Feature/Controller/NGINXTest.php
--- a/src/tests/Feature/Controller/NGINXTest.php
+++ b/src/tests/Feature/Controller/NGINXTest.php
@@ -22,7 +22,7 @@
'limit_geo' => null,
'guam_enabled' => null,
]);
- IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
+ IP4Net::where('net_number', inet_pton('128.0.0.0'))->delete();
$this->useServicesUrl();
}
@@ -36,7 +36,7 @@
'limit_geo' => null,
'guam_enabled' => null,
]);
- IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
+ IP4Net::where('net_number', inet_pton('128.0.0.0'))->delete();
parent::tearDown();
}
@@ -60,8 +60,8 @@
'Auth-Protocol' => 'imap',
'Auth-Ssl' => 'on',
'Auth-User' => 'john@kolab.org',
- 'Client-Ip' => '127.0.0.1',
- 'Host' => '127.0.0.1',
+ 'Client-Ip' => '128.0.0.1',
+ 'Host' => '128.0.0.1',
'Auth-SSL' => 'on',
'Auth-SSL-Verify' => 'SUCCESS',
'Auth-SSL-Subject' => '/CN=example.com',
@@ -148,7 +148,7 @@
);
// 2-FA with accepted auth attempt
- $authAttempt = AuthAttempt::recordAuthAttempt($john, "127.0.0.1");
+ $authAttempt = AuthAttempt::recordAuthAttempt($john, '128.0.0.1');
$authAttempt->accept();
$response = $this->withHeaders($headers)->get("api/webhooks/nginx");
@@ -171,7 +171,7 @@
$john->setSettings(['limit_geo' => '["PL","US"]']);
$headers['Auth-Protocol'] = 'imap';
- $headers['Client-Ip'] = '127.0.0.1';
+ $headers['Client-Ip'] = '128.0.0.1';
$response = $this->withHeaders($headers)->get("api/webhooks/nginx");
$response->assertStatus(200);
@@ -183,8 +183,8 @@
// Geo-lockin (success)
IP4Net::create([
- 'net_number' => '127.0.0.0',
- 'net_broadcast' => '127.255.255.255',
+ 'net_number' => '128.0.0.0',
+ 'net_broadcast' => '128.255.255.255',
'net_mask' => 8,
'country' => 'US',
'rir_name' => 'test',
@@ -225,10 +225,10 @@
$headers = [
'Php-Auth-Pw' => $pass,
'Php-Auth-User' => 'john@kolab.org',
- 'X-Forwarded-For' => '127.0.0.1',
+ 'X-Forwarded-For' => '128.0.0.1',
'X-Forwarded-Proto' => 'https',
'X-Original-Uri' => '/iRony/',
- 'X-Real-Ip' => '127.0.0.1',
+ 'X-Real-Ip' => '128.0.0.1',
];
// Pass
@@ -282,7 +282,7 @@
);
// 2-FA with accepted auth attempt
- $authAttempt = AuthAttempt::recordAuthAttempt($john, "127.0.0.1");
+ $authAttempt = AuthAttempt::recordAuthAttempt($john, '128.0.0.1');
$authAttempt->accept();
$response = $this->withHeaders($headers)->get("api/webhooks/nginx-httpauth");
diff --git a/src/tests/Feature/Controller/PasswordResetTest.php b/src/tests/Feature/Controller/PasswordResetTest.php
--- a/src/tests/Feature/Controller/PasswordResetTest.php
+++ b/src/tests/Feature/Controller/PasswordResetTest.php
@@ -18,14 +18,14 @@
$this->deleteTestUser('passwordresettest@' . \config('app.domain'));
- IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
+ IP4Net::where('net_number', inet_pton('128.0.0.0'))->delete();
}
protected function tearDown(): void
{
$this->deleteTestUser('passwordresettest@' . \config('app.domain'));
- IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
+ IP4Net::where('net_number', inet_pton('128.0.0.0'))->delete();
parent::tearDown();
}
@@ -142,7 +142,7 @@
$user->setSetting('limit_geo', json_encode(['US']));
$user->setSetting('external_email', 'ext@email.com');
- $headers['X-Client-IP'] = '127.0.0.2';
+ $headers['X-Client-IP'] = '128.0.0.2';
$post = ['email' => 'passwordresettest@' . \config('app.domain')];
$response = $this->withHeaders($headers)->post('/api/auth/password-reset/init', $post);
@@ -155,8 +155,8 @@
$this->assertSame("The request location is not allowed.", $json['errors']['email']);
IP4Net::create([
- 'net_number' => '127.0.0.0',
- 'net_broadcast' => '127.255.255.255',
+ 'net_number' => '128.0.0.0',
+ 'net_broadcast' => '128.255.255.255',
'net_mask' => 8,
'country' => 'US',
'rir_name' => 'test',
diff --git a/src/tests/Feature/Controller/PolicyTest.php b/src/tests/Feature/Controller/PolicyTest.php
--- a/src/tests/Feature/Controller/PolicyTest.php
+++ b/src/tests/Feature/Controller/PolicyTest.php
@@ -19,11 +19,11 @@
{
parent::setUp();
- $this->clientAddress = '127.0.0.100';
+ $this->clientAddress = '128.0.0.100';
$this->net = IP4Net::create([
- 'net_number' => '127.0.0.0',
- 'net_broadcast' => '127.255.255.255',
+ 'net_number' => '128.0.0.0',
+ 'net_broadcast' => '128.255.255.255',
'net_mask' => 8,
'country' => 'US',
'rir_name' => 'test',
diff --git a/src/tests/Feature/Controller/SignupTest.php b/src/tests/Feature/Controller/SignupTest.php
--- a/src/tests/Feature/Controller/SignupTest.php
+++ b/src/tests/Feature/Controller/SignupTest.php
@@ -44,7 +44,7 @@
SI::truncate();
SignupToken::truncate();
Plan::where('title', 'test')->delete();
- IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
+ IP4Net::where('net_number', inet_pton('128.0.0.0'))->delete();
VatRate::query()->delete();
ReferralProgram::query()->delete();
}
@@ -64,7 +64,7 @@
SI::truncate();
SignupToken::truncate();
Plan::where('title', 'test')->delete();
- IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
+ IP4Net::where('net_number', inet_pton('128.0.0.0'))->delete();
VatRate::query()->delete();
ReferralProgram::query()->delete();
@@ -1177,8 +1177,8 @@
]);
IP4Net::create([
- 'net_number' => '127.0.0.0',
- 'net_broadcast' => '127.255.255.255',
+ 'net_number' => '128.0.0.0',
+ 'net_broadcast' => '128.255.255.255',
'net_mask' => 8,
'country' => 'CH',
'rir_name' => 'test',
@@ -1187,7 +1187,7 @@
// Test with mode=mandate plan, and valid voucher code
$post['voucher'] = 'TEST';
- $headers = ['X-Client-IP' => '127.0.0.2'];
+ $headers = ['X-Client-IP' => '128.0.0.2'];
$response = $this->withHeaders($headers)->post('/api/auth/signup/validate', $post);
$response->assertStatus(200);
diff --git a/src/tests/Feature/Policy/GreylistTest.php b/src/tests/Feature/Policy/GreylistTest.php
--- a/src/tests/Feature/Policy/GreylistTest.php
+++ b/src/tests/Feature/Policy/GreylistTest.php
@@ -56,7 +56,7 @@
$request = new Greylist([
'sender' => 'someone@sender.domain',
'recipient' => $this->domainOwner->email,
- 'client_address' => '127.128.129.130',
+ 'client_address' => '128.128.129.130',
'client_name' => 'some.mx',
]);
diff --git a/src/tests/Unit/UtilsTest.php b/src/tests/Unit/UtilsTest.php
--- a/src/tests/Unit/UtilsTest.php
+++ b/src/tests/Unit/UtilsTest.php
@@ -17,7 +17,7 @@
public function testCountryForIP(): void
{
// Create some network records, the tables might be empty
- IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
+ IP4Net::where('net_number', inet_pton('128.0.0.0'))->delete();
IP6Net::where('net_number', inet_pton('2001:db8::ff00:42:0'))->delete();
$this->assertSame('', Utils::countryForIP('127.0.0.1', ''));
@@ -26,8 +26,8 @@
$this->assertSame('CH', Utils::countryForIP('2001:db8::ff00:42:1'));
IP4Net::create([
- 'net_number' => '127.0.0.0',
- 'net_broadcast' => '127.255.255.255',
+ 'net_number' => '128.0.0.0',
+ 'net_broadcast' => '128.255.255.255',
'net_mask' => 8,
'country' => 'US',
'rir_name' => 'test',
@@ -43,12 +43,12 @@
'serial' => 1,
]);
- $this->assertSame('US', Utils::countryForIP('127.0.0.1', ''));
- $this->assertSame('US', Utils::countryForIP('127.0.0.1'));
+ $this->assertSame('US', Utils::countryForIP('128.0.0.1', ''));
+ $this->assertSame('US', Utils::countryForIP('128.0.0.1'));
$this->assertSame('PL', Utils::countryForIP('2001:db8::ff00:42:1', ''));
$this->assertSame('PL', Utils::countryForIP('2001:db8::ff00:42:1'));
- IP4Net::where('net_number', inet_pton('127.0.0.0'))->delete();
+ IP4Net::where('net_number', inet_pton('128.0.0.0'))->delete();
IP6Net::where('net_number', inet_pton('2001:db8::ff00:42:0'))->delete();
}
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
Fri, Apr 3, 11:11 PM (13 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18827232
Default Alt Text
D5268.1775257895.diff (21 KB)

Event Timeline