Page MenuHomePhorge

D925.1775891553.diff
No OneTemporary

Authored By
Unknown
Size
41 KB
Referenced Files
None
Subscribers
None

D925.1775891553.diff

diff --git a/src/app/Backends/IMAP.php b/src/app/Backends/IMAP.php
new file mode 100644
--- /dev/null
+++ b/src/app/Backends/IMAP.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Backends;
+
+use App\Domain;
+use App\User;
+
+class IMAP
+{
+ /**
+ * Check if an account is set up
+ *
+ * @param string $username User login (email address)
+ *
+ * @return bool
+ */
+ public static function verifyAccount(string $username): bool
+ {
+ // TODO
+ return true;
+ }
+}
diff --git a/src/app/Domain.php b/src/app/Domain.php
--- a/src/app/Domain.php
+++ b/src/app/Domain.php
@@ -8,14 +8,18 @@
{
// we've simply never heard of this domain
public const STATUS_NEW = 1 << 0;
- // it's been activated -- mutually exclusive with new?
+ // it's been activated
public const STATUS_ACTIVE = 1 << 1;
- // ownership of the domain has been confirmed -- mutually exclusive with new?
- public const STATUS_CONFIRMED = 1 << 2;
// domain has been suspended.
- public const STATUS_SUSPENDED = 1 << 3;
- // domain has been deleted -- can not be active any more.
- public const STATUS_DELETED = 1 << 4;
+ public const STATUS_SUSPENDED = 1 << 2;
+ // domain has been deleted
+ public const STATUS_DELETED = 1 << 3;
+ // ownership of the domain has been confirmed
+ public const STATUS_CONFIRMED = 1 << 4;
+ // domain has been verified that it exists in DNS
+ public const STATUS_VERIFIED = 1 << 5;
+ // domain has been created in LDAP
+ public const STATUS_LDAP_READY = 1 << 6;
// open for public registration
public const TYPE_PUBLIC = 1 << 0;
@@ -33,10 +37,6 @@
'type'
];
- //protected $guarded = [
- // "status"
- //];
-
public function entitlement()
{
return $this->morphOne('App\Entitlement', 'entitleable');
@@ -124,6 +124,16 @@
return $this->type & self::TYPE_PUBLIC;
}
+ /**
+ * Returns whether this domain is registered in LDAP.
+ *
+ * @return bool
+ */
+ public function isLdapReady(): bool
+ {
+ return $this->status & self::STATUS_LDAP_READY;
+ }
+
/**
* Returns whether this domain is suspended.
*
@@ -134,6 +144,17 @@
return $this->status & self::STATUS_SUSPENDED;
}
+ /**
+ * Returns whether this (external) domain has been verified
+ * to exist in DNS.
+ *
+ * @return bool
+ */
+ public function isVerified(): bool
+ {
+ return $this->status & self::STATUS_VERIFIED;
+ }
+
/**
* Domain status mutator
*
@@ -149,6 +170,8 @@
self::STATUS_CONFIRMED,
self::STATUS_SUSPENDED,
self::STATUS_DELETED,
+ self::STATUS_LDAP_READY,
+ self::STATUS_VERIFIED,
];
foreach ($allowed_values as $value) {
@@ -164,4 +187,104 @@
$this->attributes['status'] = $new_status;
}
+
+ /**
+ * Ownership verification by checking for a TXT (or CNAME) record
+ * in the domain's DNS (that matches the verification hash).
+ *
+ * @return bool True if verification was successful, false otherwise
+ * @throws \Exception Throws exception on DNS or DB errors
+ */
+ public function confirm(): bool
+ {
+ if ($this->isConfirmed()) {
+ return true;
+ }
+
+ $hash = $this->hash();
+ $confirmed = false;
+
+ // Get DNS records and find a matching TXT entry
+ $records = \dns_get_record($this->namespace, DNS_TXT);
+
+ if ($records === false) {
+ throw new \Exception("Failed to get DNS record for $domain");
+ }
+
+ foreach ($records as $record) {
+ if ($record['txt'] === $hash) {
+ $confirmed = true;
+ break;
+ }
+ }
+
+ // Get DNS records and find a matching CNAME entry
+ // Note: some servers resolve every non-existing name
+ // so we need to define left and right side of the CNAME record
+ // i.e.: kolab-verify IN CNAME <hash>.domain.tld.
+ if (!$confirmed) {
+ $cname = $this->hash(true) . '.' . $this->namespace;
+ $records = \dns_get_record('kolab-verify.' . $this->namespace, DNS_CNAME);
+
+ if ($records === false) {
+ throw new \Exception("Failed to get DNS record for $domain");
+ }
+
+ foreach ($records as $records) {
+ if ($records['target'] === $cname) {
+ $confirmed = true;
+ break;
+ }
+ }
+ }
+
+ if ($confirmed) {
+ $this->status |= Domain::STATUS_CONFIRMED;
+ $this->save();
+ }
+
+ return $confirmed;
+ }
+
+ /**
+ * Generate a verification hash for this domain
+ *
+ * @param bool $short Return short version (with kolab-verify= prefix)
+ *
+ * @return string Verification hash
+ */
+ public function hash($short = false): string
+ {
+ $hash = \md5('hkccp-verify-' . $this->namespace . $this->id);
+
+ return $short ? $hash : "kolab-verify=$hash";
+ }
+
+ /**
+ * Verify if a domain exists in DNS
+ *
+ * @return bool True if registered, False otherwise
+ * @throws \Exception Throws exception on DNS or DB errors
+ */
+ public function verify(): bool
+ {
+ if ($this->isVerified()) {
+ return true;
+ }
+
+ $record = \dns_get_record($domain, DNS_SOA);
+
+ if ($record === false) {
+ throw new \Exception("Failed to get DNS record for $domain");
+ }
+
+ if (!empty($record)) {
+ $this->status |= Domain::STATUS_VERIFIED;
+ $this->save();
+
+ return true;
+ }
+
+ return false;
+ }
}
diff --git a/src/app/Http/Controllers/API/UsersController.php b/src/app/Http/Controllers/API/UsersController.php
--- a/src/app/Http/Controllers/API/UsersController.php
+++ b/src/app/Http/Controllers/API/UsersController.php
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
+use App\Domain;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@@ -73,7 +74,12 @@
*/
public function info()
{
- return response()->json($this->guard()->user());
+ $user = $this->guard()->user();
+ $response = $user->toArray();
+
+ $response['statusInfo'] = self::statusInfo($user);
+
+ return response()->json($response);
}
/**
@@ -170,6 +176,59 @@
return \App\User::find($id);
}
+ /**
+ * User status (extended) information
+ *
+ * @param \App\User $user User object
+ *
+ * @return array Status information
+ */
+ public static function statusInfo(User $user): array
+ {
+ $status = 'new';
+ $process = [];
+ $steps = [
+ 'user-new' => true,
+ 'user-ldap-ready' => 'isLdapReady',
+ 'user-imap-ready' => 'isImapReady',
+ ];
+
+ if ($user->isDeleted()) {
+ $status = 'deleted';
+ } elseif ($user->isSuspended()) {
+ $status = 'suspended';
+ } elseif ($user->isActive()) {
+ $status = 'active';
+ }
+
+ list ($local, $domain) = explode('@', $user->email);
+ $domain = Domain::where('namespace', $domain)->first();
+
+ // If that is not a public domain, add domain specific steps
+ if (!$domain->isPublic()) {
+ $steps['domain-new'] = true;
+ $steps['domain-ldap-ready'] = 'isLdapReady';
+ $steps['domain-verified'] = 'isVerified';
+ $steps['domain-confirmed'] = 'isConfirmed';
+ }
+
+ // Create a process check list
+ foreach ($steps as $step_name => $func) {
+ $object = strpos($step_name, 'user-') === 0 ? $user : $domain;
+
+ $process[] = [
+ 'label' => $step_name,
+ 'title' => __("app.process-{$step_name}"),
+ 'state' => is_bool($func) ? $func : $object->{$func}(),
+ ];
+ }
+
+ return [
+ 'process' => $process,
+ 'status' => $status,
+ ];
+ }
+
/**
* Get the guard to be used during authentication.
*
diff --git a/src/app/Jobs/ProcessDomainCreate.php b/src/app/Jobs/ProcessDomainCreate.php
--- a/src/app/Jobs/ProcessDomainCreate.php
+++ b/src/app/Jobs/ProcessDomainCreate.php
@@ -43,6 +43,11 @@
*/
public function handle()
{
- LDAP::createDomain($this->domain);
+ if (!$this->domain->isLdapReady()) {
+ LDAP::createDomain($this->domain);
+
+ $this->domain->status |= Domain::STATUS_LDAP_READY;
+ $this->domain->save();
+ }
}
}
diff --git a/src/app/Jobs/ProcessDomainCreate.php b/src/app/Jobs/ProcessDomainVerify.php
copy from src/app/Jobs/ProcessDomainCreate.php
copy to src/app/Jobs/ProcessDomainVerify.php
--- a/src/app/Jobs/ProcessDomainCreate.php
+++ b/src/app/Jobs/ProcessDomainVerify.php
@@ -2,15 +2,14 @@
namespace App\Jobs;
-use App\Backends\LDAP;
use App\Domain;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
-use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
-class ProcessDomainCreate implements ShouldQueue
+class ProcessDomainVerify implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
@@ -43,6 +42,10 @@
*/
public function handle()
{
- LDAP::createDomain($this->domain);
+ $this->domain->verify();
+
+ // TODO: What should happen if the domain is not registered yet?
+ // Should we start a new job with some specified delay?
+ // Or we just give the user a button to start verification again?
}
}
diff --git a/src/app/Jobs/ProcessUserCreate.php b/src/app/Jobs/ProcessUserCreate.php
--- a/src/app/Jobs/ProcessUserCreate.php
+++ b/src/app/Jobs/ProcessUserCreate.php
@@ -44,6 +44,11 @@
*/
public function handle()
{
- LDAP::createUser($this->user);
+ if (!$this->user->isLdapReady()) {
+ LDAP::createUser($this->user);
+
+ $this->user->status |= User::STATUS_LDAP_READY;
+ $this->user->save();
+ }
}
}
diff --git a/src/app/Jobs/ProcessUserCreate.php b/src/app/Jobs/ProcessUserVerify.php
copy from src/app/Jobs/ProcessUserCreate.php
copy to src/app/Jobs/ProcessUserVerify.php
--- a/src/app/Jobs/ProcessUserCreate.php
+++ b/src/app/Jobs/ProcessUserVerify.php
@@ -2,7 +2,6 @@
namespace App\Jobs;
-use App\Backends\LDAP;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -10,7 +9,7 @@
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
-class ProcessUserCreate implements ShouldQueue
+class ProcessUserVerify implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
@@ -44,6 +43,12 @@
*/
public function handle()
{
- LDAP::createUser($this->user);
+ if (!$this->user->isImapReady()) {
+ if (IMAP::verifyAccount($this->user->email)) {
+ $this->user->status |= User::STATUS_IMAP_READY;
+ $this->user->status |= User::STATUS_ACTIVE;
+ $this->user->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
@@ -22,6 +22,8 @@
break;
}
}
+
+ $domain->status |= Domain::STATUS_NEW;
}
/**
@@ -33,7 +35,12 @@
*/
public function created(Domain $domain)
{
- \App\Jobs\ProcessDomainCreate::dispatch($domain);
+ // Create domain record in LDAP, then check if it exists in DNS
+ $chain = [
+ new \App\Jobs\ProcessDomainVerify($domain),
+ ];
+
+ \App\Jobs\ProcessDomainCreate::withChain($chain)->dispatch($domain);
}
/**
diff --git a/src/app/Observers/UserObserver.php b/src/app/Observers/UserObserver.php
--- a/src/app/Observers/UserObserver.php
+++ b/src/app/Observers/UserObserver.php
@@ -24,6 +24,9 @@
break;
}
}
+
+ $user->status |= User::STATUS_NEW;
+
// can't dispatch job here because it'll fail serialization
}
@@ -54,7 +57,12 @@
$user->wallets()->create();
- \App\Jobs\ProcessUserCreate::dispatch($user);
+ // Create user record in LDAP, then check if the account is created in IMAP
+ $chain = [
+ new \App\Jobs\ProcessUserVerify($user),
+ ];
+
+ \App\Jobs\ProcessUserCreate::withChain($chain)->dispatch($user);
}
/**
diff --git a/src/app/User.php b/src/app/User.php
--- a/src/app/User.php
+++ b/src/app/User.php
@@ -18,6 +18,20 @@
use NullableFields;
use UserSettingsTrait;
+ // a new user, default on creation
+ public const STATUS_NEW = 1 << 0;
+ // it's been activated
+ public const STATUS_ACTIVE = 1 << 1;
+ // user has been suspended
+ public const STATUS_SUSPENDED = 1 << 2;
+ // user has been deleted
+ public const STATUS_DELETED = 1 << 3;
+ // user has been created in LDAP
+ public const STATUS_LDAP_READY = 1 << 4;
+ // user mailbox has been created in IMAP
+ public const STATUS_IMAP_READY = 1 << 5;
+
+
// change the default primary key type
public $incrementing = false;
protected $keyType = 'bigint';
@@ -28,7 +42,11 @@
* @var array
*/
protected $fillable = [
- 'name', 'email', 'password', 'password_ldap'
+ 'name',
+ 'email',
+ 'password',
+ 'password_ldap',
+ 'status'
];
/**
@@ -37,7 +55,9 @@
* @var array
*/
protected $hidden = [
- 'password', 'password_ldap', 'remember_token',
+ 'password',
+ 'password_ldap',
+ 'remember_token',
];
protected $nullable = [
@@ -150,6 +170,82 @@
return $user;
}
+ public function getJWTIdentifier()
+ {
+ return $this->getKey();
+ }
+
+ public function getJWTCustomClaims()
+ {
+ return [];
+ }
+
+ /**
+ * Returns whether this domain is active.
+ *
+ * @return bool
+ */
+ public function isActive(): bool
+ {
+ return $this->status & self::STATUS_ACTIVE;
+ }
+
+ /**
+ * Returns whether this domain is deleted.
+ *
+ * @return bool
+ */
+ public function isDeleted(): bool
+ {
+ return $this->status & self::STATUS_DELETED;
+ }
+
+ /**
+ * Returns whether this (external) domain has been verified
+ * to exist in DNS.
+ *
+ * @return bool
+ */
+ public function isImapReady(): bool
+ {
+ return $this->status & self::STATUS_IMAP_READY;
+ }
+
+ /**
+ * Returns whether this user is registered in LDAP.
+ *
+ * @return bool
+ */
+ public function isLdapReady(): bool
+ {
+ return $this->status & self::STATUS_LDAP_READY;
+ }
+
+ /**
+ * Returns whether this user is new.
+ *
+ * @return bool
+ */
+ public function isNew(): bool
+ {
+ return $this->status & self::STATUS_NEW;
+ }
+
+ /**
+ * Returns whether this domain is suspended.
+ *
+ * @return bool
+ */
+ public function isSuspended(): bool
+ {
+ return $this->status & self::STATUS_SUSPENDED;
+ }
+
+ /**
+ * Any (additional) properties of this user.
+ *
+ * @return \App\UserSetting[]
+ */
public function settings()
{
return $this->hasMany('App\UserSetting', 'user_id');
@@ -175,16 +271,6 @@
return $this->hasMany('App\Wallet');
}
- public function getJWTIdentifier()
- {
- return $this->getKey();
- }
-
- public function getJWTCustomClaims()
- {
- return [];
- }
-
public function setPasswordAttribute($password)
{
if (!empty($password)) {
@@ -204,4 +290,36 @@
);
}
}
+
+ /**
+ * User status mutator
+ *
+ * @throws \Exception
+ */
+ public function setStatusAttribute($status)
+ {
+ $new_status = 0;
+
+ $allowed_values = [
+ self::STATUS_NEW,
+ self::STATUS_ACTIVE,
+ self::STATUS_SUSPENDED,
+ self::STATUS_DELETED,
+ self::STATUS_LDAP_READY,
+ self::STATUS_IMAP_READY,
+ ];
+
+ foreach ($allowed_values as $value) {
+ if ($status & $value) {
+ $new_status |= $value;
+ $status ^= $value;
+ }
+ }
+
+ if ($status > 0) {
+ throw new \Exception("Invalid user status: {$status}");
+ }
+
+ $this->attributes['status'] = $new_status;
+ }
}
diff --git a/src/database/migrations/2014_10_12_000000_create_users_table.php b/src/database/migrations/2014_10_12_000000_create_users_table.php
--- a/src/database/migrations/2014_10_12_000000_create_users_table.php
+++ b/src/database/migrations/2014_10_12_000000_create_users_table.php
@@ -22,6 +22,7 @@
$table->timestamp('email_verified_at')->nullable();
$table->string('password')->nullable();
$table->string('password_ldap')->nullable();
+ $table->smallinteger('status');
$table->rememberToken();
$table->timestamps();
diff --git a/src/resources/lang/en/app.php b/src/resources/lang/en/app.php
--- a/src/resources/lang/en/app.php
+++ b/src/resources/lang/en/app.php
@@ -12,4 +12,11 @@
'planbutton' => 'Choose :plan',
+ 'process-user-new' => 'User registered',
+ 'process-user-ldap-ready' => 'User created',
+ 'process-user-imap-ready' => 'User mailbox created',
+ 'process-domain-new' => 'Custom domain registered',
+ 'process-domain-ldap-ready' => 'Custom domain created',
+ 'process-domain-verified' => 'Custom domain verified',
+ 'process-domain-confirmed' => 'Custom domain ownership verified',
];
diff --git a/src/resources/vue/components/Dashboard.vue b/src/resources/vue/components/Dashboard.vue
--- a/src/resources/vue/components/Dashboard.vue
+++ b/src/resources/vue/components/Dashboard.vue
@@ -1,12 +1,23 @@
<template>
<div class="container">
- <div class="row">
- <div class="col-md-8 col-md-offset-2">
- <div class="panel panel-default">
- <div class="panel-heading">Dashboard</div>
- <div class="panel-body">
- <p>Data: {{ data }}</p>
- </div>
+ <div id="dashboard-box" class="card">
+ <div class="card-body">
+ <div class="card-title">Dashboard</div>
+ <div class="card-text">
+ <p>{{ data }}</p>
+ </div>
+ </div>
+ </div>
+ <div id="status-box" class="card">
+ <div class="card-body">
+ <div class="card-title">Status</div>
+ <div class="card-text">
+ <ul style="list-style: none; padding: 0">
+ <li v-for="item in statusProcess">
+ <span v-if="item.state">&check;</span><span v-else>&cir;</span>
+ {{ item.title }}
+ </li>
+ </ul>
</div>
</div>
</div>
@@ -17,7 +28,8 @@
export default {
data() {
return {
- data: 'nothing'
+ data: [],
+ statusProcess: []
}
},
mounted() {
@@ -27,8 +39,42 @@
}
})
.then(response => {
- this.data = response.data.data
+ this.data = response.data
+ this.parseStatusInfo(response.data.statusInfo)
+ delete this.data.statusInfo
})
+ },
+ methods: {
+ // Displays account status information
+ parseStatusInfo(info) {
+ this.statusProcess = info.process
+
+ // Update status process info every 10 seconds
+ // FIXME: This probably should have some limit, or the interval
+ // should grow (well, until it could be done with websocket notifications)
+ if (info.status != 'active') {
+ setTimeout(() => {
+ // Stop updates after user logged out
+ // FIXME: Definitely stopping such requests after logout
+ // adding the header to all axios requests calls
+ // for some more general solution
+ let token = localStorage.getItem('token')
+ if (!token) {
+ return;
+ }
+
+ axios.get('/api/auth/info', {
+ headers: { Authorization: 'Bearer ' + token }
+ }).then(response => {
+ this.parseStatusInfo(response.data.statusInfo)
+ }).catch(error => {
+ this.parseStatusInfo(info)
+ })
+ }, 10000);
+ }
+
+ return info
+ }
}
}
</script>
diff --git a/src/tests/Feature/Controller/UsersTest.php b/src/tests/Feature/Controller/UsersTest.php
--- a/src/tests/Feature/Controller/UsersTest.php
+++ b/src/tests/Feature/Controller/UsersTest.php
@@ -2,6 +2,8 @@
namespace Tests\Feature\Controller;
+use App\Http\Controllers\API\UsersController;
+use App\Domain;
use App\User;
use Illuminate\Support\Str;
use Tests\TestCase;
@@ -15,29 +17,32 @@
{
parent::setUp();
- $user = User::where('email', 'UsersControllerTest1@UsersControllerTest.com')->delete();
+ User::where('email', 'UsersControllerTest1@userscontroller.com')->delete();
+ Domain::where('namespace', 'userscontroller.com')->delete();
}
/**
- * {@inheritDoc}
+ * Test fetching current user info
*/
- public function tearDown(): void
- {
- $user = User::where('email', 'UsersControllerTest1@UsersControllerTest.com')->delete();
- }
-
- public function testListUsers(): void
+ public function testInfo(): void
{
- $user = $this->getTestUser('UsersControllerTest1@UsersControllerTest.com');
+ $user = $this->getTestUser('UsersControllerTest1@userscontroller.com');
+ $domain = $this->getTestDomain('userscontroller.com', [
+ 'status' => Domain::STATUS_NEW,
+ 'type' => Domain::TYPE_PUBLIC,
+ ]);
- $response = $this->actingAs($user)->get("api/v4/users");
-
- $response->assertJsonCount(1);
+ $response = $this->actingAs($user)->get("api/auth/info");
+ $json = $response->json();
$response->assertStatus(200);
+ $this->assertEquals($user->id, $json['id']);
+ $this->assertEquals($user->email, $json['email']);
+ $this->assertEquals(User::STATUS_NEW, $json['status']);
+ $this->assertTrue(is_array($json['statusInfo']));
}
- public function testUserEntitlements()
+ public function testIndex(): void
{
$userA = $this->getTestUser('UserEntitlement2A@UserEntitlement.com');
@@ -50,4 +55,91 @@
$response = $this->actingAs($user)->get("/api/v4/users/{$userA->id}");
$response->assertStatus(404);
}
+
+ public function testLogin(): void
+ {
+ // TODO
+ $this->markTestIncomplete();
+ }
+
+ public function testLogout(): void
+ {
+ // TODO
+ $this->markTestIncomplete();
+ }
+
+ public function testRefresh(): void
+ {
+ // TODO
+ $this->markTestIncomplete();
+ }
+
+ public function testShow(): void
+ {
+ $user = $this->getTestUser('UsersControllerTest1@userscontroller.com');
+ $domain = $this->getTestDomain('userscontroller.com', [
+ 'status' => Domain::STATUS_NEW,
+ 'type' => Domain::TYPE_PUBLIC,
+ ]);
+
+ $user->status = User::STATUS_NEW;
+ $user->save();
+
+ $result = UsersController::statusInfo($user);
+
+ $this->assertSame('new', $result['status']);
+ $this->assertCount(3, $result['process']);
+ $this->assertSame('user-new', $result['process'][0]['label']);
+ $this->assertSame(true, $result['process'][0]['state']);
+ $this->assertSame('user-ldap-ready', $result['process'][1]['label']);
+ $this->assertSame(false, $result['process'][1]['state']);
+ $this->assertSame('user-imap-ready', $result['process'][2]['label']);
+ $this->assertSame(false, $result['process'][2]['state']);
+
+ $user->status |= User::STATUS_LDAP_READY | User::STATUS_IMAP_READY;
+ $user->save();
+
+ $result = UsersController::statusInfo($user);
+
+ $this->assertSame('new', $result['status']);
+ $this->assertCount(3, $result['process']);
+ $this->assertSame('user-new', $result['process'][0]['label']);
+ $this->assertSame(true, $result['process'][0]['state']);
+ $this->assertSame('user-ldap-ready', $result['process'][1]['label']);
+ $this->assertSame(true, $result['process'][1]['state']);
+ $this->assertSame('user-imap-ready', $result['process'][2]['label']);
+ $this->assertSame(true, $result['process'][2]['state']);
+
+ $user->status |= User::STATUS_ACTIVE;
+ $user->save();
+ $domain->status |= Domain::STATUS_VERIFIED;
+ $domain->type = Domain::TYPE_EXTERNAL;
+ $domain->save();
+
+ $result = UsersController::statusInfo($user);
+
+ $this->assertSame('active', $result['status']);
+ $this->assertCount(7, $result['process']);
+ $this->assertSame('user-new', $result['process'][0]['label']);
+ $this->assertSame(true, $result['process'][0]['state']);
+ $this->assertSame('user-ldap-ready', $result['process'][1]['label']);
+ $this->assertSame(true, $result['process'][1]['state']);
+ $this->assertSame('user-imap-ready', $result['process'][2]['label']);
+ $this->assertSame(true, $result['process'][2]['state']);
+ $this->assertSame('domain-new', $result['process'][3]['label']);
+ $this->assertSame(true, $result['process'][3]['state']);
+ $this->assertSame('domain-ldap-ready', $result['process'][4]['label']);
+ $this->assertSame(false, $result['process'][4]['state']);
+ $this->assertSame('domain-verified', $result['process'][5]['label']);
+ $this->assertSame(true, $result['process'][5]['state']);
+ $this->assertSame('domain-confirmed', $result['process'][6]['label']);
+ $this->assertSame(false, $result['process'][6]['state']);
+
+ $user->status |= User::STATUS_DELETED;
+ $user->save();
+
+ $result = UsersController::statusInfo($user);
+
+ $this->assertSame('deleted', $result['status']);
+ }
}
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
@@ -17,7 +17,51 @@
{
parent::setUp();
- Domain::where('namespace', 'public-active.com')->delete();
+ Domain::where('namespace', 'public-active.com')
+ ->orWhere('namespace', 'gmail.com')->delete();
+ }
+
+ /**
+ * Test domain creating jobs
+ */
+ public function testCreateJobs(): void
+ {
+ // Fake the queue, assert that no jobs were pushed...
+ Queue::fake();
+ Queue::assertNothingPushed();
+
+ $domain = Domain::create([
+ 'namespace' => 'gmail.com',
+ 'status' => Domain::STATUS_NEW,
+ 'type' => Domain::TYPE_EXTERNAL,
+ ]);
+
+ Queue::assertPushed(\App\Jobs\ProcessDomainCreate::class, 1);
+ Queue::assertPushed(\App\Jobs\ProcessDomainCreate::class, function ($job) use ($domain) {
+ $job_domain = TestCase::getObjectProperty($job, 'domain');
+
+ return $job_domain->id === $domain->id
+ && $job_domain->namespace === $domain->namespace;
+ });
+
+ Queue::assertPushedWithChain(\App\Jobs\ProcessDomainCreate::class, [
+ \App\Jobs\ProcessDomainVerify::class,
+ ]);
+
+/*
+ FIXME: Looks like we can't really do detailed assertions on chained jobs
+ Another thing to consider is if we maybe should run these jobs
+ independently (not chained) and make sure there's no race-condition
+ in status update
+
+ Queue::assertPushed(\App\Jobs\ProcessDomainVerify::class, 1);
+ Queue::assertPushed(\App\Jobs\ProcessDomainVerify::class, function ($job) use ($domain) {
+ $job_domain = TestCase::getObjectProperty($job, 'domain');
+
+ return $job_domain->id === $domain->id
+ && $job_domain->namespace === $domain->namespace;
+ });
+*/
}
/**
@@ -29,9 +73,7 @@
$this->assertNotContains('public-active.com', $public_domains);
- // Fake the queue, assert that no jobs were pushed...
Queue::fake();
- Queue::assertNothingPushed();
$domain = Domain::create([
'namespace' => 'public-active.com',
@@ -43,14 +85,6 @@
$public_domains = Domain::getPublicDomains();
$this->assertNotContains('public-active.com', $public_domains);
- Queue::assertPushed(\App\Jobs\ProcessDomainCreate::class, 1);
- Queue::assertPushed(\App\Jobs\ProcessDomainCreate::class, function ($job) use ($domain) {
- $job_domain = TestCase::getObjectProperty($job, 'domain');
-
- return $job_domain->id === $domain->id
- && $job_domain->namespace === $domain->namespace;
- });
-
$domain = Domain::where('namespace', 'public-active.com')->first();
$domain->status = Domain::STATUS_ACTIVE;
$domain->save();
@@ -59,4 +93,26 @@
$public_domains = Domain::getPublicDomains();
$this->assertContains('public-active.com', $public_domains);
}
+
+ /**
+ * Test domain confirmation
+ *
+ * @group dns
+ */
+ public function testConfirm(): void
+ {
+ // TODO
+ $this->markTestIncomplete();
+ }
+
+ /**
+ * Test domain verification
+ *
+ * @group dns
+ */
+ public function testVerify(): void
+ {
+ // TODO
+ $this->markTestIncomplete();
+ }
}
diff --git a/src/tests/Feature/Jobs/ProcessDomainCreateTest.php b/src/tests/Feature/Jobs/ProcessDomainCreateTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Jobs/ProcessDomainCreateTest.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Tests\Feature\Jobs;
+
+use App\Jobs\ProcessDomainCreate;
+use App\Domain;
+use Illuminate\Support\Facades\Mail;
+use Tests\TestCase;
+
+class DomainCreateTest extends TestCase
+{
+ use \Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ Domain::where('namespace', 'domain-create-test.com')->delete();
+ }
+
+ /**
+ * Test job handle
+ */
+ public function testHandle(): void
+ {
+ $domain = $this->getTestDomain(
+ 'domain-create-test.com',
+ [
+ 'status' => Domain::STATUS_NEW,
+ 'type' => Domain::TYPE_EXTERNAL,
+ ]
+ );
+
+ $this->assertFalse($domain->isLdapReady());
+
+ $mock = \Mockery::mock('alias:App\Backends\LDAP');
+ $mock->shouldReceive('createDomain')
+ ->once()
+ ->with($domain)
+ ->andReturn(null);
+
+ $job = new ProcessDomainCreate($domain);
+ $job->handle();
+
+ $this->assertTrue($domain->fresh()->isLdapReady());
+ }
+}
diff --git a/src/tests/Feature/Jobs/ProcessDomainVerifyTest.php b/src/tests/Feature/Jobs/ProcessDomainVerifyTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Jobs/ProcessDomainVerifyTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Tests\Feature\Jobs;
+
+use App\Jobs\ProcessDomainVerify;
+use App\Domain;
+use Illuminate\Support\Facades\Mail;
+use Tests\TestCase;
+
+class DomainVerifyTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ Domain::where('namespace', 'gmail.com')
+ ->orWhere('namespace', 'some-non-existing-domain.fff')
+ ->delete();
+ }
+
+ /**
+ * Test job handle (existing domain)
+ *
+ * @group dns
+ */
+ public function testHandle(): void
+ {
+ $domain = $this->getTestDomain(
+ 'gmail.com',
+ [
+ 'status' => Domain::STATUS_NEW,
+ 'type' => Domain::TYPE_EXTERNAL,
+ ]
+ );
+
+ $this->assertFalse($domain->isVerified());
+
+ $job = new ProcessDomainVerify($domain);
+ $job->handle();
+
+ $this->assertTrue($domain->fresh()->isVerified());
+ }
+
+ /**
+ * Test job handle (non-existing domain)
+ *
+ * @group dns
+ */
+ public function testHandleNonExisting(): void
+ {
+ $domain = $this->getTestDomain(
+ 'some-non-existing-domain.fff',
+ [
+ 'status' => Domain::STATUS_NEW,
+ 'type' => Domain::TYPE_EXTERNAL,
+ ]
+ );
+
+ $this->assertFalse($domain->isVerified());
+
+ $job = new ProcessDomainVerify($domain);
+ $job->handle();
+
+ $this->assertFalse($domain->fresh()->isVerified());
+ }
+}
diff --git a/src/tests/Feature/Jobs/UserCreateTest.php b/src/tests/Feature/Jobs/UserCreateTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Jobs/UserCreateTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Tests\Feature\Jobs;
+
+use App\Jobs\ProcessUserCreate;
+use App\User;
+use Illuminate\Support\Facades\Mail;
+use Tests\TestCase;
+
+class UserCreateTest extends TestCase
+{
+ use \Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ User::where('email', 'new-job-user@' . \config('app.domain'))->delete();
+ }
+
+ /**
+ * Test job handle
+ */
+ public function testHandle(): void
+ {
+ $user = $this->getTestUser('new-job-user@' . \config('app.domain'));
+
+ $this->assertFalse($user->isLdapReady());
+
+ $mock = \Mockery::mock('alias:App\Backends\LDAP');
+ $mock->shouldReceive('createUser')
+ ->once()
+ ->with($user)
+ ->andReturn(null);
+
+ $job = new ProcessUserCreate($user);
+ $job->handle();
+
+ $this->assertTrue($user->fresh()->isLdapReady());
+ }
+}
diff --git a/src/tests/Feature/Jobs/UserVerify.php b/src/tests/Feature/Jobs/UserVerify.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Jobs/UserVerify.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Tests\Feature\Jobs;
+
+use App\Jobs\ProcessUserVerify;
+use App\User;
+use Illuminate\Support\Facades\Mail;
+use Tests\TestCase;
+
+class UserVerifyTest extends TestCase
+{
+ use \Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ User::where('email', 'new-job-user@' . \config('app.domain'))->delete();
+ }
+
+ /**
+ * Test job handle
+ */
+ public function testHandle(): void
+ {
+ $user = $this->getTestUser('new-job-user@' . \config('app.domain'));
+
+ $this->assertFalse($user->isImapReady());
+
+ $mock = \Mockery::mock('alias:App\Backends\IMAP');
+ $mock->shouldReceive('verifyAccount')
+ ->once()
+ ->with($user->email)
+ ->andReturn(false);
+
+ $job = new ProcessUserVerify($user);
+ $job->handle();
+
+ $this->assertTrue($user->fresh()->isImapReady() === false);
+
+ $mock->shouldReceive('verifyAccount')
+ ->once()
+ ->with($user->email)
+ ->andReturn(true);
+
+ $job->handle();
+
+ $this->assertTrue($user->fresh()->isImapReady());
+ }
+}
diff --git a/src/tests/Feature/UserTest.php b/src/tests/Feature/UserTest.php
--- a/src/tests/Feature/UserTest.php
+++ b/src/tests/Feature/UserTest.php
@@ -35,6 +35,24 @@
return $job_user->id === $user->id
&& $job_user->email === $user->email;
});
+
+ Queue::assertPushedWithChain(\App\Jobs\ProcessUserCreate::class, [
+ \App\Jobs\ProcessUserVerify::class,
+ ]);
+/*
+ FIXME: Looks like we can't really do detailed assertions on chained jobs
+ Another thing to consider is if we maybe should run these jobs
+ independently (not chained) and make sure there's no race-condition
+ in status update
+
+ Queue::assertPushed(\App\Jobs\ProcessUserVerify::class, 1);
+ Queue::assertPushed(\App\Jobs\ProcessUserVerify::class, function ($job) use ($user) {
+ $job_user = TestCase::getObjectProperty($job, 'user');
+
+ return $job_user->id === $user->id
+ && $job_user->email === $user->email;
+ });
+*/
}
/**
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
@@ -10,7 +10,7 @@
/**
* Test basic Domain funtionality
*/
- public function testDomainStatus()
+ public function testDomainStatus(): void
{
$statuses = [
Domain::STATUS_NEW,
@@ -18,6 +18,8 @@
Domain::STATUS_CONFIRMED,
Domain::STATUS_SUSPENDED,
Domain::STATUS_DELETED,
+ Domain::STATUS_LDAP_READY,
+ Domain::STATUS_VERIFIED,
];
$domains = \App\Utils::powerSet($statuses);
@@ -36,13 +38,30 @@
$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));
}
}
+ /**
+ * Test setStatusAttribute exception
+ */
+ public function testDomainStatusInvalid(): void
+ {
+ $this->expectException(\Exception::class);
+
+ $domain = new Domain(
+ [
+ 'namespace' => 'test.com',
+ 'status' => 1234567,
+ ]
+ );
+ }
+
/**
* Test basic Domain funtionality
*/
- public function testDomainType()
+ public function testDomainType(): void
{
$types = [
Domain::TYPE_PUBLIC,
@@ -66,4 +85,25 @@
$this->assertTrue($domain->isExternal() === in_array(Domain::TYPE_EXTERNAL, $domain_types));
}
}
+
+ /**
+ * Test domain hash generation
+ */
+ public function testHash(): void
+ {
+ $domain = new Domain([
+ 'namespace' => 'test.com',
+ 'status' => Domain::STATUS_NEW,
+ ]);
+
+ $hash1 = $domain->hash(true);
+
+ $this->assertRegExp('/^[a-f0-9]{32}$/', $hash1);
+
+ $hash2 = $domain->hash();
+
+ $this->assertRegExp('/^kolab-verify=[a-f0-9]{32}$/', $hash2);
+
+ $this->assertSame($hash1, str_replace('kolab-verify=', '', $hash2));
+ }
}
diff --git a/src/tests/Unit/UserTest.php b/src/tests/Unit/UserTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Unit/UserTest.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Tests\Unit;
+
+use App\User;
+use Tests\TestCase;
+
+class UserTest extends TestCase
+{
+ /**
+ * Test basic User funtionality
+ */
+ public function testUserStatus()
+ {
+ $statuses = [
+ User::STATUS_NEW,
+ User::STATUS_ACTIVE,
+ User::STATUS_SUSPENDED,
+ User::STATUS_DELETED,
+ User::STATUS_IMAP_READY,
+ User::STATUS_LDAP_READY,
+ ];
+
+ $users = \App\Utils::powerSet($statuses);
+
+ foreach ($users as $user_statuses) {
+ $user = new User(
+ [
+ 'email' => 'user@email.com',
+ 'status' => \array_sum($user_statuses),
+ ]
+ );
+
+ $this->assertTrue($user->isNew() === in_array(User::STATUS_NEW, $user_statuses));
+ $this->assertTrue($user->isActive() === in_array(User::STATUS_ACTIVE, $user_statuses));
+ $this->assertTrue($user->isSuspended() === in_array(User::STATUS_SUSPENDED, $user_statuses));
+ $this->assertTrue($user->isDeleted() === in_array(User::STATUS_DELETED, $user_statuses));
+ $this->assertTrue($user->isLdapReady() === in_array(User::STATUS_LDAP_READY, $user_statuses));
+ $this->assertTrue($user->isImapReady() === in_array(User::STATUS_IMAP_READY, $user_statuses));
+ }
+ }
+
+ /**
+ * Test setStatusAttribute exception
+ */
+ public function testUserStatusInvalid(): void
+ {
+ $this->expectException(\Exception::class);
+
+ $user = new User(
+ [
+ 'email' => 'user@email.com',
+ 'status' => 1234567,
+ ]
+ );
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 11, 7:12 AM (12 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18859228
Default Alt Text
D925.1775891553.diff (41 KB)

Event Timeline