diff --git a/src/app/Backends/LDAP.php b/src/app/Backends/LDAP.php
--- a/src/app/Backends/LDAP.php
+++ b/src/app/Backends/LDAP.php
@@ -256,7 +256,7 @@
if (!self::getUserEntry($ldap, $user->email, $dn)) {
if (empty($dn)) {
- self::throwException($ldap, "Failed to create user {$user->email} in LDAP");
+ self::throwException($ldap, "Failed to create user {$user->email} in LDAP (" . __LINE__ . ")");
}
self::setUserAttributes($user, $entry);
diff --git a/src/app/Domain.php b/src/app/Domain.php
--- a/src/app/Domain.php
+++ b/src/app/Domain.php
@@ -232,11 +232,11 @@
$allowed_values = [
self::STATUS_NEW,
self::STATUS_ACTIVE,
- self::STATUS_CONFIRMED,
self::STATUS_SUSPENDED,
self::STATUS_DELETED,
- self::STATUS_LDAP_READY,
+ self::STATUS_CONFIRMED,
self::STATUS_VERIFIED,
+ self::STATUS_LDAP_READY,
];
foreach ($allowed_values as $value) {
@@ -250,6 +250,31 @@
throw new \Exception("Invalid domain status: {$status}");
}
+ if ($this->isPublic()) {
+ $this->attributes['status'] = $new_status;
+ return;
+ }
+
+ if ($new_status & self::STATUS_CONFIRMED) {
+ // if we have confirmed ownership of or management access to the domain, then we have
+ // also confirmed the domain exists in DNS.
+ $new_status |= self::STATUS_VERIFIED;
+ $new_status |= self::STATUS_ACTIVE;
+ }
+
+ if ($new_status & self::STATUS_DELETED && $new_status & self::STATUS_ACTIVE) {
+ $new_status ^= self::STATUS_ACTIVE;
+ }
+
+ if ($new_status & self::STATUS_SUSPENDED && $new_status & self::STATUS_ACTIVE) {
+ $new_status ^= self::STATUS_ACTIVE;
+ }
+
+ // if the domain is now active, it is not new anymore.
+ if ($new_status & self::STATUS_ACTIVE && $new_status & self::STATUS_NEW) {
+ $new_status ^= self::STATUS_NEW;
+ }
+
$this->attributes['status'] = $new_status;
}
@@ -349,6 +374,15 @@
/**
* Unsuspend this domain.
*
+ * The domain is unsuspended through either of the following courses of actions;
+ *
+ * * The account balance has been topped up, or
+ * * a suspected spammer has resolved their issues, or
+ * * the command-line is triggered.
+ *
+ * Therefore, we can also confidently set the domain status to 'active' should the ownership of or management
+ * access to have been confirmed before.
+ *
* @return void
*/
public function unsuspend(): void
@@ -358,6 +392,11 @@
}
$this->status ^= Domain::STATUS_SUSPENDED;
+
+ if ($this->isConfirmed() && $this->isVerified()) {
+ $this->status |= Domain::STATUS_ACTIVE;
+ }
+
$this->save();
}
diff --git a/src/app/Observers/DomainObserver.php b/src/app/Observers/DomainObserver.php
--- a/src/app/Observers/DomainObserver.php
+++ b/src/app/Observers/DomainObserver.php
@@ -26,7 +26,7 @@
$domain->namespace = \strtolower($domain->namespace);
- $domain->status |= Domain::STATUS_NEW | Domain::STATUS_ACTIVE;
+ $domain->status |= Domain::STATUS_NEW;
}
/**
diff --git a/src/phpunit.xml b/src/phpunit.xml
--- a/src/phpunit.xml
+++ b/src/phpunit.xml
@@ -13,6 +13,10 @@
tests/Unit
+
+ tests/Functional
+
+
tests/Feature
diff --git a/src/tests/Browser/StatusTest.php b/src/tests/Browser/StatusTest.php
--- a/src/tests/Browser/StatusTest.php
+++ b/src/tests/Browser/StatusTest.php
@@ -54,16 +54,20 @@
{
// Unconfirmed domain and user
$domain = Domain::where('namespace', 'kolab.org')->first();
+
if ($domain->isConfirmed()) {
$domain->status ^= Domain::STATUS_CONFIRMED;
$domain->save();
}
$john = $this->getTestUser('john@kolab.org');
+
$john->created_at = Carbon::now();
+
if ($john->isImapReady()) {
$john->status ^= User::STATUS_IMAP_READY;
}
+
$john->save();
$this->browse(function ($browser) use ($john, $domain) {
@@ -113,10 +117,13 @@
$domain->status ^= Domain::STATUS_CONFIRMED;
$domain->save();
}
+
$john->created_at = Carbon::now()->subSeconds(3600);
+
if ($john->isImapReady()) {
$john->status ^= User::STATUS_IMAP_READY;
}
+
$john->save();
$this->browse(function ($browser) use ($john, $domain) {
@@ -153,6 +160,18 @@
$domain->status = Domain::STATUS_NEW | Domain::STATUS_ACTIVE | Domain::STATUS_LDAP_READY;
$domain->save();
+ // side-step
+ $this->assertFalse($domain->isNew());
+ $this->assertTrue($domain->isActive());
+ $this->assertTrue($domain->isLdapReady());
+ $this->assertTrue($domain->isExternal());
+
+ $this->assertFalse($domain->isHosted());
+ $this->assertFalse($domain->isConfirmed());
+ $this->assertFalse($domain->isVerified());
+ $this->assertFalse($domain->isSuspended());
+ $this->assertFalse($domain->isDeleted());
+
$this->browse(function ($browser) use ($domain) {
// Test auto-refresh
$browser->on(new Dashboard())
diff --git a/src/tests/Feature/Controller/DomainsTest.php b/src/tests/Feature/Controller/DomainsTest.php
--- a/src/tests/Feature/Controller/DomainsTest.php
+++ b/src/tests/Feature/Controller/DomainsTest.php
@@ -229,14 +229,14 @@
$json = $response->json();
$this->assertTrue($json['isVerified']);
- $this->assertFalse($json['isReady']);
+ $this->assertTrue($json['isReady']);
$this->assertCount(4, $json['process']);
$this->assertSame('domain-verified', $json['process'][2]['label']);
$this->assertSame(true, $json['process'][2]['state']);
$this->assertSame('domain-confirmed', $json['process'][3]['label']);
- $this->assertSame(false, $json['process'][3]['state']);
- $this->assertSame('error', $json['status']);
- $this->assertSame('Failed to verify an ownership of a domain.', $json['message']);
+ $this->assertSame(true, $json['process'][3]['state']);
+ $this->assertSame('success', $json['status']);
+ $this->assertSame('Setup process finished successfully.', $json['message']);
// TODO: Test completing all process steps
}
diff --git a/src/tests/Feature/Controller/PaymentsStripeTest.php b/src/tests/Feature/Controller/PaymentsStripeTest.php
--- a/src/tests/Feature/Controller/PaymentsStripeTest.php
+++ b/src/tests/Feature/Controller/PaymentsStripeTest.php
@@ -541,6 +541,7 @@
$wallet->setSetting('mandate_disabled', null);
$wallet->balance = -2050;
$wallet->save();
+
$result = PaymentsController::topUpWallet($wallet);
$this->assertFalse($result);
$this->assertCount(1, $wallet->payments()->get());
diff --git a/src/tests/Feature/Controller/WalletsTest.php b/src/tests/Feature/Controller/WalletsTest.php
--- a/src/tests/Feature/Controller/WalletsTest.php
+++ b/src/tests/Feature/Controller/WalletsTest.php
@@ -89,7 +89,7 @@
public function testReceiptDownload(): void
{
$user = $this->getTestUser('wallets-controller@kolabnow.com');
- $john = $this->getTestUser('john@klab.org');
+ $john = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
// Unauth access not allowed
@@ -132,7 +132,7 @@
public function testReceipts(): void
{
$user = $this->getTestUser('wallets-controller@kolabnow.com');
- $john = $this->getTestUser('john@klab.org');
+ $john = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
$wallet->payments()->delete();
@@ -220,7 +220,7 @@
$package_kolab = \App\Package::where('title', 'kolab')->first();
$user = $this->getTestUser('wallets-controller@kolabnow.com');
$user->assignPackage($package_kolab);
- $john = $this->getTestUser('john@klab.org');
+ $john = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
// Unauth access not allowed
diff --git a/src/tests/Feature/DomainTest.php b/src/tests/Feature/DomainTest.php
--- a/src/tests/Feature/DomainTest.php
+++ b/src/tests/Feature/DomainTest.php
@@ -65,7 +65,7 @@
$this->assertSame('gmail.com', $result->namespace);
$this->assertSame($domain->id, $result->id);
$this->assertSame($domain->type, $result->type);
- $this->assertSame(Domain::STATUS_NEW | Domain::STATUS_ACTIVE, $result->status);
+ $this->assertSame(Domain::STATUS_NEW, $result->status);
}
/**
diff --git a/src/tests/Functional/Methods/DomainTest.php b/src/tests/Functional/Methods/DomainTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Functional/Methods/DomainTest.php
@@ -0,0 +1,114 @@
+domain = $this->getTestDomain(
+ 'test.domain',
+ [
+ 'status' => \App\Domain::STATUS_CONFIRMED | \App\Domain::STATUS_VERIFIED,
+ 'type' => \App\Domain::TYPE_EXTERNAL
+ ]
+ );
+ }
+
+ public function tearDown(): void
+ {
+ $this->deleteTestDomain('test.domain');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Verify we can suspend an active domain.
+ */
+ public function testSuspendForActiveDomain()
+ {
+ Queue::fake();
+
+ $this->domain->status |= \App\Domain::STATUS_ACTIVE;
+
+ $this->assertFalse($this->domain->isSuspended());
+ $this->assertTrue($this->domain->isActive());
+
+ $this->domain->suspend();
+
+ $this->assertTrue($this->domain->isSuspended());
+ $this->assertFalse($this->domain->isActive());
+ }
+
+ /**
+ * Verify we can unsuspend a suspended domain
+ */
+ public function testUnsuspendForSuspendedDomain()
+ {
+ Queue::fake();
+
+ $this->domain->status |= \App\Domain::STATUS_SUSPENDED;
+
+ $this->assertTrue($this->domain->isSuspended());
+ $this->assertFalse($this->domain->isActive());
+
+ $this->domain->unsuspend();
+
+ $this->assertFalse($this->domain->isSuspended());
+ $this->assertTrue($this->domain->isActive());
+ }
+
+ /**
+ * Verify we can unsuspend a suspended domain that wasn't confirmed
+ */
+ public function testUnsuspendForSuspendedUnconfirmedDomain()
+ {
+ Queue::fake();
+
+ $this->domain->status = \App\Domain::STATUS_NEW | \App\Domain::STATUS_SUSPENDED;
+
+ $this->assertTrue($this->domain->isNew());
+ $this->assertTrue($this->domain->isSuspended());
+ $this->assertFalse($this->domain->isActive());
+ $this->assertFalse($this->domain->isConfirmed());
+ $this->assertFalse($this->domain->isVerified());
+
+ $this->domain->unsuspend();
+
+ $this->assertTrue($this->domain->isNew());
+ $this->assertFalse($this->domain->isSuspended());
+ $this->assertFalse($this->domain->isActive());
+ $this->assertFalse($this->domain->isConfirmed());
+ $this->assertFalse($this->domain->isVerified());
+ }
+
+ /**
+ * Verify we can unsuspend a suspended domain that was verified but not confirmed
+ */
+ public function testUnsuspendForSuspendedVerifiedUnconfirmedDomain()
+ {
+ Queue::fake();
+
+ $this->domain->status = \App\Domain::STATUS_NEW | \App\Domain::STATUS_SUSPENDED | \App\Domain::STATUS_VERIFIED;
+
+ $this->assertTrue($this->domain->isNew());
+ $this->assertTrue($this->domain->isSuspended());
+ $this->assertFalse($this->domain->isActive());
+ $this->assertFalse($this->domain->isConfirmed());
+ $this->assertTrue($this->domain->isVerified());
+
+ $this->domain->unsuspend();
+
+ $this->assertTrue($this->domain->isNew());
+ $this->assertFalse($this->domain->isSuspended());
+ $this->assertFalse($this->domain->isActive());
+ $this->assertFalse($this->domain->isConfirmed());
+ $this->assertTrue($this->domain->isVerified());
+ }
+
+}
diff --git a/src/tests/Unit/DomainTest.php b/src/tests/Unit/DomainTest.php
--- a/src/tests/Unit/DomainTest.php
+++ b/src/tests/Unit/DomainTest.php
@@ -24,38 +24,59 @@
$domains = \App\Utils::powerSet($statuses);
- foreach ($domains as $domain_statuses) {
+ foreach ($domains as $domainStatuses) {
$domain = new Domain(
[
'namespace' => 'test.com',
- 'status' => \array_sum($domain_statuses),
+ 'status' => \array_sum($domainStatuses),
'type' => Domain::TYPE_EXTERNAL
]
);
- $this->assertTrue($domain->isNew() === in_array(Domain::STATUS_NEW, $domain_statuses));
- $this->assertTrue($domain->isActive() === in_array(Domain::STATUS_ACTIVE, $domain_statuses));
- $this->assertTrue($domain->isConfirmed() === in_array(Domain::STATUS_CONFIRMED, $domain_statuses));
- $this->assertTrue($domain->isSuspended() === in_array(Domain::STATUS_SUSPENDED, $domain_statuses));
- $this->assertTrue($domain->isDeleted() === in_array(Domain::STATUS_DELETED, $domain_statuses));
- $this->assertTrue($domain->isLdapReady() === in_array(Domain::STATUS_LDAP_READY, $domain_statuses));
- $this->assertTrue($domain->isVerified() === in_array(Domain::STATUS_VERIFIED, $domain_statuses));
- }
- }
+ $domainStatuses = [];
- /**
- * Test setStatusAttribute exception
- */
- public function testDomainStatusInvalid(): void
- {
- $this->expectException(\Exception::class);
-
- $domain = new Domain(
- [
- 'namespace' => 'test.com',
- 'status' => 1234567,
- ]
- );
+ foreach ($statuses as $status) {
+ if ($domain->status & $status) {
+ $domainStatuses[] = $status;
+ }
+ }
+
+ $this->assertSame($domain->status, \array_sum($domainStatuses));
+
+ // either one is true, but not both
+ $this->assertSame(
+ $domain->isNew() === in_array(Domain::STATUS_NEW, $domainStatuses),
+ $domain->isActive() === in_array(Domain::STATUS_ACTIVE, $domainStatuses)
+ );
+
+ $this->assertTrue(
+ $domain->isNew() === in_array(Domain::STATUS_NEW, $domainStatuses)
+ );
+
+ $this->assertTrue(
+ $domain->isActive() === in_array(Domain::STATUS_ACTIVE, $domainStatuses)
+ );
+
+ $this->assertTrue(
+ $domain->isConfirmed() === in_array(Domain::STATUS_CONFIRMED, $domainStatuses)
+ );
+
+ $this->assertTrue(
+ $domain->isSuspended() === in_array(Domain::STATUS_SUSPENDED, $domainStatuses)
+ );
+
+ $this->assertTrue(
+ $domain->isDeleted() === in_array(Domain::STATUS_DELETED, $domainStatuses)
+ );
+
+ $this->assertTrue(
+ $domain->isLdapReady() === in_array(Domain::STATUS_LDAP_READY, $domainStatuses)
+ );
+
+ $this->assertTrue(
+ $domain->isVerified() === in_array(Domain::STATUS_VERIFIED, $domainStatuses)
+ );
+ }
}
/**
diff --git a/src/tests/Unit/Methods/DomainTest.php b/src/tests/Unit/Methods/DomainTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Unit/Methods/DomainTest.php
@@ -0,0 +1,159 @@
+domain = new \App\Domain();
+ }
+
+ /**
+ * Test lower-casing namespace attribute.
+ */
+ public function testSetNamespaceAttributeLowercases()
+ {
+ $this->domain = new \App\Domain();
+
+ $this->domain->namespace = 'UPPERCASE';
+
+ $this->assertTrue($this->domain->namespace === 'uppercase');
+ }
+
+ /**
+ * Test setting the status to something invalid
+ */
+ public function testSetStatusAttributeInvalid()
+ {
+ $this->expectException(\Exception::class);
+
+ $this->domain->status = 123456;
+ }
+
+ /**
+ * Test public domain.
+ */
+ public function testSetStatusAttributeOnPublicDomain()
+ {
+ $this->domain->{'type'} = \App\Domain::TYPE_PUBLIC;
+
+ $this->domain->status = 115;
+
+ $this->assertTrue($this->domain->status == 115);
+ }
+
+ /**
+ * Test status mutations
+ */
+ public function testSetStatusAttributeActiveMakesForNotNew()
+ {
+ $this->domain->status = \App\Domain::STATUS_NEW;
+
+ $this->assertTrue($this->domain->isNew());
+ $this->assertFalse($this->domain->isActive());
+
+ $this->domain->status |= \App\Domain::STATUS_ACTIVE;
+
+ $this->assertFalse($this->domain->isNew());
+ $this->assertTrue($this->domain->isActive());
+ }
+
+ /**
+ * Verify setting confirmed sets verified.
+ */
+ public function testSetStatusAttributeConfirmedMakesForVerfied()
+ {
+ $this->domain->status = \App\Domain::STATUS_CONFIRMED;
+
+ $this->assertTrue($this->domain->isConfirmed());
+ $this->assertTrue($this->domain->isVerified());
+ }
+
+ /**
+ * Verify setting confirmed sets active.
+ */
+ public function testSetStatusAttributeConfirmedMakesForActive()
+ {
+ $this->domain->status = \App\Domain::STATUS_CONFIRMED;
+
+ $this->assertTrue($this->domain->isConfirmed());
+ $this->assertTrue($this->domain->isActive());
+ }
+
+ /**
+ * Verify setting deleted drops active.
+ */
+ public function testSetStatusAttributeDeletedVoidsActive()
+ {
+ $this->domain->status = \App\Domain::STATUS_ACTIVE;
+
+ $this->assertTrue($this->domain->isActive());
+ $this->assertFalse($this->domain->isNew());
+ $this->assertFalse($this->domain->isDeleted());
+
+ $this->domain->status |= \App\Domain::STATUS_DELETED;
+
+ $this->assertFalse($this->domain->isActive());
+ $this->assertFalse($this->domain->isNew());
+ $this->assertTrue($this->domain->isDeleted());
+ }
+
+ /**
+ * Verify setting suspended drops active.
+ */
+ public function testSetStatusAttributeSuspendedVoidsActive()
+ {
+ $this->domain->status = \App\Domain::STATUS_ACTIVE;
+
+ $this->assertTrue($this->domain->isActive());
+ $this->assertFalse($this->domain->isSuspended());
+
+ $this->domain->status |= \App\Domain::STATUS_SUSPENDED;
+
+ $this->assertFalse($this->domain->isActive());
+ $this->assertTrue($this->domain->isSuspended());
+ }
+
+ /**
+ * Verify we can suspend a suspended domain without disaster.
+ *
+ * This doesn't change anything to trigger a save.
+ */
+ public function testSuspendForSuspendedDomain()
+ {
+ $this->domain->status = \App\Domain::STATUS_ACTIVE;
+
+ $this->domain->status |= \App\Domain::STATUS_SUSPENDED;
+
+ $this->assertTrue($this->domain->isSuspended());
+ $this->assertFalse($this->domain->isActive());
+
+ $this->domain->suspend();
+
+ $this->assertTrue($this->domain->isSuspended());
+ $this->assertFalse($this->domain->isActive());
+ }
+
+ /**
+ * Verify we can unsuspend an active (unsuspended) domain
+ *
+ * This doesn't change anything to trigger a save.
+ */
+ public function testUnsuspendForActiveDomain()
+ {
+ $this->domain->status = \App\Domain::STATUS_ACTIVE;
+
+ $this->assertFalse($this->domain->isSuspended());
+ $this->assertTrue($this->domain->isActive());
+
+ $this->domain->unsuspend();
+
+ $this->assertFalse($this->domain->isSuspended());
+ $this->assertTrue($this->domain->isActive());
+ }
+}