diff --git a/bin/phpstan b/bin/phpstan new file mode 100755 --- /dev/null +++ b/bin/phpstan @@ -0,0 +1,11 @@ +#!/bin/bash + +cwd=$(dirname $0) + +pushd ${cwd}/../src/ + +php -dmemory_limit=256M \ + vendor/bin/phpstan \ + analyse + +popd diff --git a/bin/phpunit b/bin/phpunit new file mode 100755 --- /dev/null +++ b/bin/phpunit @@ -0,0 +1,15 @@ +#!/bin/bash + +cwd=$(dirname $0) + +pushd ${cwd}/../src/ + +php -dzend_extension=xdebug.so \ + vendor/bin/phpunit \ + --exclude-group imap \ + --stop-on-defect \ + --stop-on-error \ + --stop-on-failure \ + --testsuite Unit,Feature + +popd diff --git a/bin/quickstart.sh b/bin/quickstart.sh --- a/bin/quickstart.sh +++ b/bin/quickstart.sh @@ -43,10 +43,10 @@ docker-compose up -d kolab mariadb redis pushd ${base_dir}/src/ +cp .env.example .env composer install npm install find bootstrap/cache/ -type f ! -name ".gitignore" -delete -cp .env.example .env ./artisan key:generate ./artisan jwt:secret -f ./artisan clear-compiled diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml --- a/phpdoc.dist.xml +++ b/phpdoc.dist.xml @@ -11,8 +11,10 @@ src/app/ src/database/ + src/database/factories/ src/database/migrations/ src/database/seeds/ + src/resources/ src/resources/ diff --git a/src/.env.example b/src/.env.example --- a/src/.env.example +++ b/src/.env.example @@ -25,9 +25,8 @@ IMAP_URI=ssl://127.0.0.1:993 IMAP_ADMIN_LOGIN=cyrus-admin IMAP_ADMIN_PASSWORD=Welcome2KolabSystems -IMAP_VERIFY_PEER=true -IMAP_VERIFY_NAME=true -IMAP_CAFILE=null +IMAP_VERIFY_HOST=false +IMAP_VERIFY_PEER=false LDAP_BASE_DN="dc=mgmt,dc=com" LDAP_DOMAIN_BASE_DN="ou=Domains,dc=mgmt,dc=com" diff --git a/src/app/Auth/LDAPUserProvider.php b/src/app/Auth/LDAPUserProvider.php --- a/src/app/Auth/LDAPUserProvider.php +++ b/src/app/Auth/LDAPUserProvider.php @@ -3,32 +3,69 @@ namespace App\Auth; use App\User; -use Carbon\Carbon; use Illuminate\Auth\EloquentUserProvider; use Illuminate\Support\Facades\Hash; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\UserProvider; +/** + * A user provider that integrates an LDAP deployment. + */ class LDAPUserProvider extends EloquentUserProvider implements UserProvider { + /** + * Retrieve the user by its ID. + * + * @param string $identifier The unique ID for the user to attempt to retrieve. + * + * @return User|null + */ public function retrieveById($identifier) { - return parent::retrieveById($identifier); + $authenticatable = parent::retrieveById($identifier); + return $authenticatable->user; } + /** + * Retrieve the user by its credentials. + * + * Please note that this function also validates the password. + * + * @param array $credentials An array containing the email and password. + * + * @return User|null + */ public function retrieveByCredentials(array $credentials) { - $entries = User::where('email', '=', $credentials['email']); + $entries = User::where('email', '=', $credentials['email'])->get(); if ($entries->count() == 1) { $user = $entries->select('id', 'email', 'password', 'password_ldap')->first(); + if (!$this->validateCredentials($user, $credentials)) { + return null; + } + return $user; + } else { + if ($entries->count() > 1) { + \Log::warning("Multiple entries for {$credentials['email']}"); + } else { + \Log::warning("No entries for {$credentials['email']}"); + } } return null; } + /** + * Validate the credentials for a user. + * + * @param Authenticatable $user The user. + * @param array $credentials The credentials. + * + * @return bool + */ public function validateCredentials(Authenticatable $user, array $credentials) { $authenticated = false; @@ -46,6 +83,8 @@ if ($hash == $user->password_ldap) { $authenticated = true; } + } else { + \Log::error("Incomplete credentials for {$user->email}"); } } @@ -53,6 +92,8 @@ // TODO: Update password if necessary, examine whether writing to // user->password is sufficient? if ($authenticated) { + \Log::info("Successful authentication for {$user->email}"); + $user->password = $credentials['password']; $user->save(); } else { diff --git a/src/app/Backends/IMAP.php b/src/app/Backends/IMAP.php --- a/src/app/Backends/IMAP.php +++ b/src/app/Backends/IMAP.php @@ -89,8 +89,8 @@ 'socket_options' => [ 'ssl' => [ 'verify_peer' => \config('imap.verify_peer'), - 'verify_peer_name' => \config('imap.verify_name'), - 'cafile' => \config('imap.cafile'), + 'verify_peer_name' => \config('imap.verify_peer'), + 'verify_host' => \config('imap.verify_host') ], ], ], 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 @@ -162,7 +162,7 @@ * * @param \App\User $user The user account to create. * - * @return void + * @return bool|void */ public static function createUser(User $user) { @@ -213,12 +213,56 @@ // } + public static function deleteDomain($domain) + { + $config = self::getConfig('admin'); + $ldap = self::initLDAP($config); + + $hosted_root_dn = \config('ldap.hosted.root_dn'); + $mgmt_root_dn = \config('ldap.admin.root_dn'); + + $domain_base_dn = "ou={$domain->namespace},{$hosted_root_dn}"; + + if ($ldap->get_entry($domain_base_dn)) { + $ldap->delete_entry_recursive($domain_base_dn); + } + + if ($ldap_domain = $ldap->find_domain($domain->namespace)) { + if ($ldap->get_entry($ldap_domain['dn'])) { + $ldap->delete_entry($ldap_domain['dn']); + } + } + } + + public static function deleteUser($user) + { + $config = self::getConfig('admin'); + $ldap = self::initLDAP($config); + + list($_local, $_domain) = explode('@', $user->email, 2); + + $domain = $ldap->find_domain($_domain); + + if (!$domain) { + return false; + } + + $base_dn = $ldap->domain_root_dn($_domain); + $dn = "uid={$user->email},ou=People,{$base_dn}"; + + if (!$ldap->get_entry($dn)) { + return false; + } + + $ldap->delete_entry($dn); + } + /** * Update a user in LDAP. * * @param \App\User $user The user account to update. * - * @return void + * @return bool|void */ public static function updateUser(User $user) { diff --git a/src/app/Console/Commands/PackageSkusCommand.php b/src/app/Console/Commands/PackageSkusCommand.php --- a/src/app/Console/Commands/PackageSkusCommand.php +++ b/src/app/Console/Commands/PackageSkusCommand.php @@ -39,12 +39,12 @@ */ public function handle() { - $packages = Package::get(); + $packages = Package::all(); foreach ($packages as $package) { $this->info(sprintf("Package: %s", $package->title)); - foreach ($package->skus()->get() as $sku) { + foreach ($package->skus as $sku) { $this->info(sprintf(" SKU: %s (%d)", $sku->title, $sku->pivot->qty)); } } diff --git a/src/app/Console/Commands/PlanPackagesCommand.php b/src/app/Console/Commands/PlanPackagesCommand.php --- a/src/app/Console/Commands/PlanPackagesCommand.php +++ b/src/app/Console/Commands/PlanPackagesCommand.php @@ -39,14 +39,14 @@ */ public function handle() { - $plans = Plan::get(); + $plans = Plan::all(); foreach ($plans as $plan) { $this->info(sprintf("Plan: %s", $plan->title)); $plan_costs = 0; - foreach ($plan->packages()->get() as $package) { + foreach ($plan->packages as $package) { $qtyMin = $package->pivot->qty_min; $qtyMax = $package->pivot->qty_max; @@ -65,7 +65,7 @@ ) ); - foreach ($package->skus()->get() as $sku) { + foreach ($package->skus as $sku) { $this->info(sprintf(" SKU: %s (%d)", $sku->title, $sku->pivot->qty)); } diff --git a/src/app/Console/Commands/UserDomains.php b/src/app/Console/Commands/UserDomains.php --- a/src/app/Console/Commands/UserDomains.php +++ b/src/app/Console/Commands/UserDomains.php @@ -40,8 +40,6 @@ */ public function handle() { - DB::enableQueryLog(); - $user = User::where('email', $this->argument('userid'))->first(); $this->info("Found user: {$user->id}"); @@ -49,7 +47,5 @@ foreach ($user->domains() as $domain) { $this->info("Domain: {$domain->namespace}"); } - - dd(DB::getQueryLog()); } } diff --git a/src/app/Console/Commands/UserEntitlementsCommand.php b/src/app/Console/Commands/UserEntitlementsCommand.php --- a/src/app/Console/Commands/UserEntitlementsCommand.php +++ b/src/app/Console/Commands/UserEntitlementsCommand.php @@ -2,7 +2,7 @@ namespace App\Console\Commands; -use App\Domain; +use App\Sku; use App\User; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; @@ -44,19 +44,19 @@ $this->info("Found user: {$user->id}"); - $entitlements = $user->entitlements()->get(); + $skus_counted = []; - foreach ($entitlements as $entitlement) { - //yes: dd($entitlement); - $_entitleable = $entitlement->entitleable; - - if ($_entitleable instanceof Domain) { - $this->info(sprintf("Domain: %s", $_entitleable->namespace)); + foreach ($user->entitlements as $entitlement) { + if (!array_key_exists($entitlement->sku_id, $skus_counted)) { + $skus_counted[$entitlement->sku_id] = 1; + } else { + $skus_counted[$entitlement->sku_id] += 1; } + } - if ($_entitleable instanceof User) { - $this->info(sprintf("User: %s", $_entitleable->email)); - } + foreach ($skus_counted as $id => $qty) { + $sku = Sku::find($id); + $this->info("SKU: {$sku->title} ({$qty})"); } } } diff --git a/src/app/Domain.php b/src/app/Domain.php --- a/src/app/Domain.php +++ b/src/app/Domain.php @@ -3,9 +3,17 @@ namespace App; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; +/** + * The eloquent definition of a Domain. + * + * @property string $namespace + */ class Domain extends Model { + use SoftDeletes; + // we've simply never heard of this domain public const STATUS_NEW = 1 << 0; // it's been activated @@ -17,7 +25,7 @@ // 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; + public const STATUS_VERIFIED = 1 << 5; // domain has been created in LDAP public const STATUS_LDAP_READY = 1 << 6; @@ -33,6 +41,7 @@ public const HASH_CNAME = 3; public $incrementing = false; + protected $keyType = 'bigint'; protected $fillable = [ @@ -65,7 +74,7 @@ */ public function isActive(): bool { - return $this->status & self::STATUS_ACTIVE; + return ($this->status & self::STATUS_ACTIVE) == true; } /** @@ -75,7 +84,7 @@ */ public function isConfirmed(): bool { - return $this->status & self::STATUS_CONFIRMED; + return ($this->status & self::STATUS_CONFIRMED) == true; } /** @@ -85,7 +94,7 @@ */ public function isDeleted(): bool { - return $this->status & self::STATUS_DELETED; + return ($this->status & self::STATUS_DELETED) == true; } /** @@ -95,7 +104,7 @@ */ public function isExternal(): bool { - return $this->type & self::TYPE_EXTERNAL; + return ($this->type & self::TYPE_EXTERNAL) == true; } /** @@ -105,7 +114,7 @@ */ public function isHosted(): bool { - return $this->type & self::TYPE_HOSTED; + return ($this->type & self::TYPE_HOSTED) == true; } /** @@ -115,7 +124,7 @@ */ public function isNew(): bool { - return $this->status & self::STATUS_NEW; + return ($this->status & self::STATUS_NEW) == true; } /** @@ -125,7 +134,7 @@ */ public function isPublic(): bool { - return $this->type & self::TYPE_PUBLIC; + return ($this->type & self::TYPE_PUBLIC) == true; } /** @@ -135,7 +144,7 @@ */ public function isLdapReady(): bool { - return $this->status & self::STATUS_LDAP_READY; + return ($this->status & self::STATUS_LDAP_READY) == true; } /** @@ -145,7 +154,7 @@ */ public function isSuspended(): bool { - return $this->status & self::STATUS_SUSPENDED; + return ($this->status & self::STATUS_SUSPENDED) == true; } /** @@ -154,12 +163,11 @@ * * @return bool */ -/* public function isVerified(): bool { - return $this->status & self::STATUS_VERIFIED; + return ($this->status & self::STATUS_VERIFIED) == true; } -*/ + /** * Domain status mutator * @@ -176,7 +184,7 @@ self::STATUS_SUSPENDED, self::STATUS_DELETED, self::STATUS_LDAP_READY, -// self::STATUS_VERIFIED, + self::STATUS_VERIFIED, ]; foreach ($allowed_values as $value) { @@ -232,7 +240,7 @@ $records = \dns_get_record('kolab-verify.' . $this->namespace, DNS_CNAME); if ($records === false) { - throw new \Exception("Failed to get DNS record for $domain"); + throw new \Exception("Failed to get DNS record for {$this->namespace}"); } foreach ($records as $records) { @@ -277,14 +285,13 @@ * @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($this->namespace, DNS_SOA); + $record = \dns_get_record($this->namespace, DNS_ANY); if ($record === false) { throw new \Exception("Failed to get DNS record for {$this->namespace}"); @@ -299,5 +306,4 @@ return false; } -*/ } diff --git a/src/app/Entitlement.php b/src/app/Entitlement.php --- a/src/app/Entitlement.php +++ b/src/app/Entitlement.php @@ -40,7 +40,7 @@ ]; /** - * Principally entitleable objects such as 'Domain' or 'Mailbox'. + * Principally entitleable objects such as 'Domain' or 'User'. * * @return mixed */ @@ -52,7 +52,7 @@ /** * The SKU concerned. * - * @return Sku + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function sku() { @@ -62,7 +62,7 @@ /** * The owner of this entitlement. * - * @return User + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function owner() { @@ -72,7 +72,7 @@ /** * The wallet this entitlement is being billed to * - * @return Wallet + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function wallet() { diff --git a/src/app/Handlers/Base.php b/src/app/Handlers/Base.php new file mode 100644 --- /dev/null +++ b/src/app/Handlers/Base.php @@ -0,0 +1,14 @@ +sku_id)->active) { \Log::error("Sku not active"); diff --git a/src/app/Handlers/Resource.php b/src/app/Handlers/Resource.php --- a/src/app/Handlers/Resource.php +++ b/src/app/Handlers/Resource.php @@ -4,7 +4,7 @@ use App\Sku; -class Resource +class Resource extends \App\Handlers\Base { public static function entitleableClass() { diff --git a/src/app/Handlers/SharedFolder.php b/src/app/Handlers/SharedFolder.php --- a/src/app/Handlers/SharedFolder.php +++ b/src/app/Handlers/SharedFolder.php @@ -4,7 +4,7 @@ use App\Sku; -class SharedFolder +class SharedFolder extends \App\Handlers\Base { public static function entitleableClass() { diff --git a/src/app/Handlers/Storage.php b/src/app/Handlers/Storage.php --- a/src/app/Handlers/Storage.php +++ b/src/app/Handlers/Storage.php @@ -2,32 +2,16 @@ namespace App\Handlers; -use App\Quota; -use App\Sku; -use App\User; - -class Storage +class Storage extends \App\Handlers\Base { - public static function createDefaultEntitleable(User $user) - { - $quota = new Quota(); - $quota->user_id = $user->id; - $quota->save(); - - return $quota->id; - } - public static function entitleableClass() { - return Quota::class; + return null; } - public static function preReq($entitlement, $user) + public static function preReq($entitlement, $object) { - if (!Sku::find($entitlement->sku_id)->active) { - \Log::error("Sku not active"); - return false; - } + // TODO: The storage can not be modified to below what is already consumed. return true; } diff --git a/src/app/Http/Controllers/API/DomainsController.php b/src/app/Http/Controllers/API/DomainsController.php --- a/src/app/Http/Controllers/API/DomainsController.php +++ b/src/app/Http/Controllers/API/DomainsController.php @@ -12,7 +12,7 @@ /** * Display a listing of the resource. * - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse */ public function index() { @@ -22,7 +22,7 @@ /** * Show the form for creating a new resource. * - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse */ public function create() { @@ -34,7 +34,7 @@ * * @param int $id Domain identifier * - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse|void */ public function confirm($id) { @@ -57,7 +57,7 @@ * * @param int $id * - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse */ public function destroy($id) { @@ -93,7 +93,7 @@ * * @param int $id Domain identifier * - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse|void */ public function show($id) { @@ -197,7 +197,7 @@ /** * Check if the current user has access to the domain * - * @param \App\Domain Domain + * @param \App\Domain $domain The domain * * @return bool True if current user has access, False otherwise */ diff --git a/src/app/Http/Controllers/API/PasswordResetController.php b/src/app/Http/Controllers/API/PasswordResetController.php --- a/src/app/Http/Controllers/API/PasswordResetController.php +++ b/src/app/Http/Controllers/API/PasswordResetController.php @@ -24,7 +24,7 @@ * * Verifies user email, sends verification email message. * - * @param Illuminate\Http\Request HTTP request + * @param \Illuminate\Http\Request $request HTTP request * * @return \Illuminate\Http\JsonResponse JSON response */ @@ -63,7 +63,7 @@ /** * Validation of the verification code. * - * @param Illuminate\Http\Request HTTP request + * @param \Illuminate\Http\Request $request HTTP request * * @return \Illuminate\Http\JsonResponse JSON response */ @@ -106,7 +106,7 @@ /** * Password change * - * @param Illuminate\Http\Request HTTP request + * @param \Illuminate\Http\Request $request HTTP request * * @return \Illuminate\Http\JsonResponse JSON response */ diff --git a/src/app/Http/Controllers/API/SignupController.php b/src/app/Http/Controllers/API/SignupController.php --- a/src/app/Http/Controllers/API/SignupController.php +++ b/src/app/Http/Controllers/API/SignupController.php @@ -29,7 +29,7 @@ /** * Returns plans definitions for signup. * - * @param Illuminate\Http\Request HTTP request + * @param \Illuminate\Http\Request $request HTTP request * * @return \Illuminate\Http\JsonResponse JSON response */ @@ -55,7 +55,7 @@ * Verifies user name and email/phone, sends verification email/sms message. * Returns the verification code. * - * @param Illuminate\Http\Request HTTP request + * @param \Illuminate\Http\Request $request HTTP request * * @return \Illuminate\Http\JsonResponse JSON response */ @@ -102,7 +102,7 @@ /** * Validation of the verification code. * - * @param Illuminate\Http\Request HTTP request + * @param \Illuminate\Http\Request $request HTTP request * * @return \Illuminate\Http\JsonResponse JSON response */ @@ -153,7 +153,7 @@ /** * Finishes the signup process by creating the user account. * - * @param Illuminate\Http\Request HTTP request + * @param \Illuminate\Http\Request $request HTTP request * * @return \Illuminate\Http\JsonResponse JSON response */ @@ -218,15 +218,14 @@ 'status' => Domain::STATUS_NEW, 'type' => Domain::TYPE_EXTERNAL, ]); - } - // Create SKUs (after domain) - foreach ($plan->packages as $package) { - foreach ($package->skus as $sku) { - $sku->registerEntitlement($user, is_object($domain) ? [$domain] : []); - } + $domain_package = \App\Package::where('title', 'domain-hosting')->first(); + $domain_package->assign($domain, $user); } + $user_package = \App\Package::where('title', 'kolab')->first(); + $user_package->assign($user); + // Save the external email and plan in user settings $user->setSettings([ 'external_email' => $user_email, @@ -244,8 +243,8 @@ /** * Checks if the input string is a valid email address or a phone number * - * @param string $email Email address or phone number - * @param bool &$is_phone Will be set to True if the string is valid phone number + * @param string $input Email address or phone number + * @param bool $is_phone Will have been set to True if the string is valid phone number * * @return string Error message label on validation error */ @@ -297,7 +296,7 @@ /** * Login (kolab identity) validation * - * @param string $email Login (local part of an email address) + * @param string $login Login (local part of an email address) * @param string $domain Domain name * @param bool $external Enables additional checks for domain part * 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 @@ -46,7 +46,7 @@ * * The user themself, and other user entitlements. * - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse */ public function index() { @@ -141,11 +141,11 @@ } /** - * Display the specified resource. + * Display information on the user account specified by $id. * * @param int $id The account to show information for. * - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse|void */ public function show($id) { @@ -155,25 +155,13 @@ return abort(403); } - $result = false; - - $user->entitlements()->each( - function ($entitlement) { - if ($entitlement->user_id == $id) { - $result = true; - } - } - ); - - if ($user->id == $id) { - $result = true; - } - - if (!$result) { + // TODO: check whether or not the user is allowed + // for now, only allow self. + if ($user->id != $id) { return abort(404); } - return \App\User::find($id); + return response()->json($user); } /** diff --git a/src/app/Jobs/UserDelete.php b/src/app/Jobs/DomainDelete.php copy from src/app/Jobs/UserDelete.php copy to src/app/Jobs/DomainDelete.php --- a/src/app/Jobs/UserDelete.php +++ b/src/app/Jobs/DomainDelete.php @@ -2,20 +2,22 @@ namespace App\Jobs; +use App\Backends\LDAP; +use App\Domain; use Illuminate\Bus\Queueable; -use Illuminate\Queue\SerializesModels; -use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Queue\SerializesModels; +use Illuminate\Queue\InteractsWithQueue; -class UserDelete implements ShouldQueue +class DomainDelete implements ShouldQueue { use Dispatchable; use InteractsWithQueue; use Queueable; use SerializesModels; - protected $user; + protected $domain; public $tries = 5; @@ -25,13 +27,13 @@ /** * Create a new job instance. * - * @param \App\User $user The user to delete. + * @param Domain $domain The domain to delete. * * @return void */ - public function __construct(\App\User $user) + public function __construct(Domain $domain) { - $this->user = $user; + $this->domain = $domain; } /** @@ -41,6 +43,6 @@ */ public function handle() { - // + LDAP::deleteDomain($this->domain); } } diff --git a/src/app/Jobs/UserDelete.php b/src/app/Jobs/UserDelete.php --- a/src/app/Jobs/UserDelete.php +++ b/src/app/Jobs/UserDelete.php @@ -2,6 +2,8 @@ namespace App\Jobs; +use App\Backends\LDAP; +use App\User; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; @@ -25,13 +27,13 @@ /** * Create a new job instance. * - * @param \App\User $user The user to delete. + * @param int $user_id The ID of the user to delete. * * @return void */ - public function __construct(\App\User $user) + public function __construct(int $user_id) { - $this->user = $user; + $this->user = User::withTrashed()->find($user_id); } /** @@ -41,6 +43,6 @@ */ public function handle() { - // + LDAP::deleteUser($this->user); } } diff --git a/src/app/Mail/PasswordReset.php b/src/app/Mail/PasswordReset.php --- a/src/app/Mail/PasswordReset.php +++ b/src/app/Mail/PasswordReset.php @@ -19,7 +19,7 @@ /** * Create a new message instance. * - * @param \App\VerificationCode A verification code object + * @param \App\VerificationCode $code A verification code object * * @return void */ 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 @@ -36,16 +36,14 @@ public function created(Domain $domain) { // Create domain record in LDAP, then check if it exists in DNS -/* - $chain = [ - new \App\Jobs\DomainVerify($domain), - ]; - - \App\Jobs\DomainCreate::withChain($chain)->dispatch($domain); -*/ \App\Jobs\DomainCreate::dispatch($domain); } + public function deleting(Domain $domain) + { + // + } + /** * Handle the domain "updated" event. * @@ -67,7 +65,7 @@ */ public function deleted(Domain $domain) { - // + \App\Jobs\DomainDelete::dispatch($domain); } /** diff --git a/src/app/Observers/EntitlementObserver.php b/src/app/Observers/EntitlementObserver.php --- a/src/app/Observers/EntitlementObserver.php +++ b/src/app/Observers/EntitlementObserver.php @@ -19,7 +19,7 @@ * * @param Entitlement $entitlement The entitlement being created. * - * @return void + * @return bool|null */ public function creating(Entitlement $entitlement) { 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 @@ -65,6 +65,11 @@ \App\Jobs\UserCreate::withChain($chain)->dispatch($user); } + public function deleted(User $user) + { + // + } + /** * Handle the "deleting" event. * @@ -74,7 +79,16 @@ */ public function deleting(User $user) { - // TODO \App\Jobs\UserDelete::dispatch($user); + // Entitlements do not have referential integrity on the entitled object, so this is our + // way of doing an onDelete('cascade') without the foreign key. + $entitlements = \App\Entitlement::where('entitleable_id', $user->id) + ->where('entitleable_type', \App\User::class)->get(); + + foreach ($entitlements as $entitlement) { + $entitlement->delete(); + } + + \App\Jobs\UserDelete::dispatch($user->id); } /** @@ -88,7 +102,7 @@ */ public function retrieving(User $user) { - // TODO \App\Jobs\UserRead::dispatch($user); + // TODO \App\Jobs\UserRead::dispatch($user); } /** diff --git a/src/app/Package.php b/src/app/Package.php --- a/src/app/Package.php +++ b/src/app/Package.php @@ -34,6 +34,40 @@ 'discount_rate' ]; + public function assign($object, $user = null) + { + // if user == null, $object is a user ;-) + if ($user === null) { + $user = $object; + } + + $entitleable_type = null; + + if ($object instanceof \App\Domain) { + $entitleable_type = \App\Domain::class; + } + + if ($object instanceof \App\User) { + $entitleable_type = \App\User::class; + } + + $wallet_id = $user->wallets()->get()[0]->id; + + foreach ($this->skus as $sku) { + for ($i = $sku->pivot->qty; $i > 0; $i--) { + \App\Entitlement::create( + [ + 'owner_id' => $user->id, + 'wallet_id' => $wallet_id, + 'sku_id' => $sku->id, + 'entitleable_id' => $entitleable_type ? $object->id : null, + 'entitleable_type' => $entitleable_type + ] + ); + } + } + } + public function cost() { $costs = 0; diff --git a/src/app/PackageSku.php b/src/app/PackageSku.php --- a/src/app/PackageSku.php +++ b/src/app/PackageSku.php @@ -4,6 +4,9 @@ use Illuminate\Database\Eloquent\Relations\Pivot; +/** + * Link SKUs to Packages. + */ class PackageSku extends Pivot { protected $fillable = [ diff --git a/src/app/Plan.php b/src/app/Plan.php --- a/src/app/Plan.php +++ b/src/app/Plan.php @@ -11,6 +11,8 @@ * * A "Family Plan" as such may exist of "2 or more Kolab packages", * and apply a discount for the third and further Kolab packages. + * + * @property \App\Package[] $packages */ class Plan extends Model { @@ -58,6 +60,15 @@ return $costs; } + /** + * The relationship to packages. + * + * The plan contains one or more packages. Each package may have its minimum number (for + * billing) or its maximum (to allow topping out "enterprise" customers on a "small business" + * plan). + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ public function packages() { return $this->belongsToMany( @@ -74,7 +85,9 @@ } /** - * Checks if the plan has domain SKU assigned + * Checks if the plan has any type of domain SKU assigned. + * + * @return bool */ public function hasDomain(): bool { diff --git a/src/app/PlanPackage.php b/src/app/PlanPackage.php --- a/src/app/PlanPackage.php +++ b/src/app/PlanPackage.php @@ -4,6 +4,12 @@ use Illuminate\Database\Eloquent\Relations\Pivot; +/** + * Link Packages to Plans. + * + * @property integer $qty_min + * @property \App\Package $package + */ class PlanPackage extends Pivot { protected $fillable = [ @@ -24,6 +30,11 @@ 'discount_rate' => 'integer' ]; + /** + * Calculate the costs for this plan. + * + * @return integer + */ public function cost() { $costs = 0; diff --git a/src/app/Quota.php b/src/app/Quota.php deleted file mode 100644 --- a/src/app/Quota.php +++ /dev/null @@ -1,29 +0,0 @@ - 'int', - ]; - - public function entitlement() - { - return $this->morphOne('App\Entitlement', 'entitleable'); - } - - /** - * The owner of this quota entry - * - * @return \App\User - */ - public function user() - { - return $this->belongsTo('App\User', 'user_id', 'id'); - } -} diff --git a/src/app/SignupCode.php b/src/app/SignupCode.php --- a/src/app/SignupCode.php +++ b/src/app/SignupCode.php @@ -7,6 +7,8 @@ /** * The eloquent definition of a SignupCode. + * + * @property datetime $expires_at */ class SignupCode extends Model { diff --git a/src/app/Sku.php b/src/app/Sku.php --- a/src/app/Sku.php +++ b/src/app/Sku.php @@ -29,7 +29,7 @@ /** * List the entitlements that consume this SKU. * - * @return Entitlement[] + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function entitlements() { @@ -43,48 +43,4 @@ 'package_skus' )->using('App\PackageSku')->withPivot(['qty']); } - - /** - * Register (default) SKU entitlement for specified user. - * This method should be used e.g. on user creation when we have - * a set of SKUs and want to create entitlements for them (using - * default values). - */ - public function registerEntitlement(\App\User $user, array $params = []) - { - if (!$this->active) { - \Log::debug("Skipped registration of an entitlement for non-active SKU ($this->title)"); - return; - } - - $wallet = $user->wallets()->get()[0]; - - $entitlement = new \App\Entitlement(); - $entitlement->owner_id = $user->id; - $entitlement->wallet_id = $wallet->id; - $entitlement->sku_id = $this->id; - - $entitlement->entitleable_type = $this->handler_class::entitleableClass(); - - if ($user instanceof $entitlement->entitleable_type) { - $entitlement->entitleable_id = $user->id; - } else { - foreach ($params as $param) { - if ($param instanceof $entitlement->entitleable_type) { - $entitlement->entitleable_id = $param->id; - break; - } - } - } - - if (empty($entitlement->entitleable_id)) { - if (method_exists($this->handler_class, 'createDefaultEntitleable')) { - $entitlement->entitleable_id = $this->handler_class::createDefaultEntitleable($user); - } else { - throw new Exception("Failed to create an entitlement for SKU ($this->title). Missing entitleable_id."); - } - } - - $entitlement->save(); - } } diff --git a/src/app/User.php b/src/app/User.php --- a/src/app/User.php +++ b/src/app/User.php @@ -4,6 +4,7 @@ use Illuminate\Notifications\Notifiable; use Illuminate\Contracts\Auth\MustVerifyEmail; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Foundation\Auth\User as Authenticatable; use Iatstuti\Database\Support\NullableFields; use Tymon\JWTAuth\Contracts\JWTSubject; @@ -11,12 +12,16 @@ /** * The eloquent definition of a User. + * + * @property integer $id + * @property integer $status */ class User extends Authenticatable implements JWTSubject { use Notifiable; use NullableFields; use UserSettingsTrait; + use SoftDeletes; // a new user, default on creation public const STATUS_NEW = 1 << 0; @@ -78,18 +83,51 @@ /** * Any wallets on which this user is a controller. * - * @return Wallet[] + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ public function accounts() { return $this->belongsToMany( 'App\Wallet', // The foreign object definition 'user_accounts', // The table name - 'user_id', // The local foreign key - 'wallet_id' // The remote foreign key + 'user_id', // The local foreign key + 'wallet_id' // The remote foreign key ); } + /** + * Assign a package to a user. The user should not have any existing entitlements. + * + * @param \App\Package $package + * @param \App\User|null $user + * + * @return \App\User + */ + public function assignPackage($package, $user = null) + { + if (!$user) { + $user = $this; + } + + $wallet_id = $this->wallets()->get()[0]->id; + + foreach ($package->skus as $sku) { + for ($i = $sku->pivot->qty; $i > 0; $i--) { + \App\Entitlement::create( + [ + 'owner_id' => $this->id, + 'wallet_id' => $wallet_id, + 'sku_id' => $sku->id, + 'entitleable_id' => $user->id, + 'entitleable_type' => User::class + ] + ); + } + } + + return $user; + } + /** * List the domains to which this user is entitled. * @@ -97,7 +135,7 @@ */ public function domains() { - $domains = Domain::whereRaw( + $dbdomains = Domain::whereRaw( sprintf( '(type & %s) AND (status & %s)', Domain::TYPE_PUBLIC, @@ -105,19 +143,27 @@ ) )->get(); - foreach ($this->entitlements()->get() as $entitlement) { + $domains = []; + + foreach ($dbdomains as $dbdomain) { + $domains[] = $dbdomain; + } + + $entitlements = Entitlement::where('owner_id', $this->id)->get(); + + foreach ($entitlements as $entitlement) { if ($entitlement->entitleable instanceof Domain) { - $domain = Domain::find($entitlement->entitleable_id); - \Log::info("Found domain {$domain->namespace}"); + $domain = $entitlement->entitleable; + \Log::info("Found domain for {$this->email}: {$domain->namespace} (owned)"); $domains[] = $domain; } } - foreach ($this->accounts()->get() as $wallet) { - foreach ($wallet->entitlements()->get() as $entitlement) { + foreach ($this->accounts as $wallet) { + foreach ($wallet->entitlements as $entitlement) { if ($entitlement->entitleable instanceof Domain) { - $domain = Domain::find($entitlement->entitleable_id); - \Log::info("Found domain {$domain->namespace}"); + $domain = $entitlement->entitleable; + \Log::info("Found domain {$this->email}: {$domain->namespace} (charged)"); $domains[] = $domain; } } @@ -134,17 +180,19 @@ /** * Entitlements for this user. * - * @return Entitlement[] + * Note that these are entitlements that apply to the user account, and not entitlements that + * this user owns. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function entitlements() { - return $this->hasMany('App\Entitlement', 'owner_id', 'id'); + return $this->hasMany('App\Entitlement', 'entitleable_id', 'id'); } public function addEntitlement($entitlement) { - // FIXME: This contains() check looks fishy - if (!$this->entitlements()->get()->contains($entitlement)) { + if (!$this->entitlements->contains($entitlement)) { return $this->entitlements()->save($entitlement); } } @@ -187,7 +235,7 @@ */ public function isActive(): bool { - return $this->status & self::STATUS_ACTIVE; + return ($this->status & self::STATUS_ACTIVE) == true; } /** @@ -197,7 +245,7 @@ */ public function isDeleted(): bool { - return $this->status & self::STATUS_DELETED; + return ($this->status & self::STATUS_DELETED) == true; } /** @@ -208,7 +256,7 @@ */ public function isImapReady(): bool { - return $this->status & self::STATUS_IMAP_READY; + return ($this->status & self::STATUS_IMAP_READY) == true; } /** @@ -218,7 +266,7 @@ */ public function isLdapReady(): bool { - return $this->status & self::STATUS_LDAP_READY; + return ($this->status & self::STATUS_LDAP_READY) == true; } /** @@ -228,7 +276,7 @@ */ public function isNew(): bool { - return $this->status & self::STATUS_NEW; + return ($this->status & self::STATUS_NEW) == true; } /** @@ -238,13 +286,13 @@ */ public function isSuspended(): bool { - return $this->status & self::STATUS_SUSPENDED; + return ($this->status & self::STATUS_SUSPENDED) == true; } /** * Any (additional) properties of this user. * - * @return \App\UserSetting[] + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function settings() { @@ -254,7 +302,7 @@ /** * Verification codes for this user. * - * @return VerificationCode[] + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function verificationcodes() { @@ -264,13 +312,20 @@ /** * Wallets this user owns. * - * @return Wallet[] + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function wallets() { return $this->hasMany('App\Wallet'); } + /** + * User password mutator + * + * @param string $password The password in plain text. + * + * @return void + */ public function setPasswordAttribute($password) { if (!empty($password)) { @@ -281,6 +336,13 @@ } } + /** + * User LDAP password mutator + * + * @param string $password The password in plain text. + * + * @return void + */ public function setPasswordLdapAttribute($password) { if (!empty($password)) { diff --git a/src/app/UserSetting.php b/src/app/UserSetting.php --- a/src/app/UserSetting.php +++ b/src/app/UserSetting.php @@ -21,10 +21,14 @@ /** * The user to which this setting belongs. * - * @return \App\User + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function user() { - return $this->belongsTo('\App\User', 'user_id' /* local */, 'id' /* remote */); + return $this->belongsTo( + '\App\User', + 'user_id', /* local */ + 'id' /* remote */ + ); } } diff --git a/src/app/Utils.php b/src/app/Utils.php --- a/src/app/Utils.php +++ b/src/app/Utils.php @@ -49,7 +49,7 @@ */ public static function uuidStr(): string { - return (string) Uuid::uuid4(); + return Uuid::uuid4()->toString(); } private static function combine($input, $r, $index, $data, $i, &$output): void diff --git a/src/app/VerificationCode.php b/src/app/VerificationCode.php --- a/src/app/VerificationCode.php +++ b/src/app/VerificationCode.php @@ -33,7 +33,7 @@ /** * The user to which this setting belongs. * - * @return \App\User + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function user() { diff --git a/src/app/Wallet.php b/src/app/Wallet.php --- a/src/app/Wallet.php +++ b/src/app/Wallet.php @@ -10,16 +10,13 @@ * The eloquent definition of a wallet -- a container with a chunk of change. * * A wallet is owned by an {@link \App\User}. + * + * @property integer $balance */ class Wallet extends Model { use NullableFields; - /** - Our table name for the shall be 'wallet'. - - @var string - */ public $incrementing = false; protected $keyType = 'string'; @@ -53,7 +50,7 @@ */ public function addController(User $user) { - if (!$this->controllers()->get()->contains($user)) { + if (!$this->controllers->contains($user)) { $this->controllers()->save($user); } } @@ -67,7 +64,7 @@ */ public function removeController(User $user) { - if ($this->controllers()->get()->contains($user)) { + if ($this->controllers->contains($user)) { $this->controllers()->detach($user); } } @@ -107,7 +104,7 @@ /** * Controllers of this wallet. * - * @return \App\User[] + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ public function controllers() { @@ -122,7 +119,7 @@ /** * Entitlements billed to this wallet. * - * @return Entitlement[] + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function entitlements() { @@ -132,7 +129,7 @@ /** * The owner of the wallet -- the wallet is in his/her back pocket. * - * @return User + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function owner() { diff --git a/src/composer.json b/src/composer.json --- a/src/composer.json +++ b/src/composer.json @@ -37,6 +37,8 @@ "laravel/dusk": "^5.5", "mockery/mockery": "^1.0", "nunomaduro/collision": "^3.0", + "nunomaduro/larastan": "^0.4", + "phpstan/phpstan": "0.11.19", "phpunit/phpunit": "^7.5" }, "config": { diff --git a/src/config/imap.php b/src/config/imap.php --- a/src/config/imap.php +++ b/src/config/imap.php @@ -5,6 +5,5 @@ 'admin_login' => env('IMAP_ADMIN_LOGIN', 'cyrus-admin'), 'admin_password' => env('IMAP_ADMIN_PASSWORD', null), 'verify_peer' => env('IMAP_VERIFY_PEER', true), - 'verify_name' => env('IMAP_VERIFY_NAME', true), - 'cafile' => env('IMAP_CAFILE', null), + 'verify_host' => env('IMAP_VERIFY_HOST', true) ]; diff --git a/src/database/migrations/2019_10_10_095050_create_quota_table.php b/src/database/migrations/2019_10_10_095050_create_quota_table.php --- a/src/database/migrations/2019_10_10_095050_create_quota_table.php +++ b/src/database/migrations/2019_10_10_095050_create_quota_table.php @@ -13,14 +13,16 @@ */ public function up() { + /* Schema::create('quotas', function (Blueprint $table) { $table->bigIncrements('id'); $table->bigInteger('user_id'); - $table->integer('value')->default(2147483648); + $table->unsignedInteger('value')->default(2147483648); $table->timestamps(); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); }); + */ } /** diff --git a/src/database/migrations/2020_02_11_110959_add_deleted_at.php b/src/database/migrations/2020_02_11_110959_add_deleted_at.php new file mode 100644 --- /dev/null +++ b/src/database/migrations/2020_02_11_110959_add_deleted_at.php @@ -0,0 +1,51 @@ +softDeletes(); + } + ); + + Schema::table( + 'users', + function (Blueprint $table) { + $table->softDeletes(); + } + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table( + 'domains', + function (Blueprint $table) { + $table->dropColumn(['deleted_at']); + } + ); + Schema::table( + 'users', + function (Blueprint $table) { + $table->dropColumn(['deleted_at']); + } + ); + } +} diff --git a/src/database/seeds/DatabaseSeeder.php b/src/database/seeds/DatabaseSeeder.php --- a/src/database/seeds/DatabaseSeeder.php +++ b/src/database/seeds/DatabaseSeeder.php @@ -15,9 +15,9 @@ [ DomainSeeder::class, SkuSeeder::class, - UserSeeder::class, PackageSeeder::class, - PlanSeeder::class + PlanSeeder::class, + UserSeeder::class ] ); } diff --git a/src/database/seeds/DomainSeeder.php b/src/database/seeds/DomainSeeder.php --- a/src/database/seeds/DomainSeeder.php +++ b/src/database/seeds/DomainSeeder.php @@ -35,5 +35,21 @@ ] ); } + + $domains = [ + 'example.com', + 'example.net', + 'example.org' + ]; + + foreach ($domains as $domain) { + Domain::create( + [ + 'namespace' => $domain, + 'status' => Domain::STATUS_CONFIRMED + Domain::STATUS_ACTIVE, + 'type' => Domain::TYPE_EXTERNAL + ] + ); + } } } diff --git a/src/database/seeds/UserSeeder.php b/src/database/seeds/UserSeeder.php --- a/src/database/seeds/UserSeeder.php +++ b/src/database/seeds/UserSeeder.php @@ -19,12 +19,12 @@ $domain = Domain::create( [ 'namespace' => 'kolab.org', - 'status' => Domain::STATUS_NEW + Domain::STATUS_ACTIVE + Domain::STATUS_CONFIRMED, + 'status' => Domain::STATUS_NEW + Domain::STATUS_ACTIVE + Domain::STATUS_CONFIRMED + Domain::STATUS_VERIFIED, 'type' => Domain::TYPE_EXTERNAL ] ); - $user = User::create( + $john = User::create( [ 'name' => "John Doe", 'email' => 'john@kolab.org', @@ -33,38 +33,43 @@ ] ); - $user_wallets = $user->wallets()->get(); - - $sku_domain = Sku::where('title', 'domain')->first(); - $sku_mailbox = Sku::where('title', 'mailbox')->first(); - - $entitlement_domain = Entitlement::create( + $john->setSettings( [ - 'owner_id' => $user->id, - 'wallet_id' => $user_wallets[0]->id, - 'sku_id' => $sku_domain->id, - 'entitleable_id' => $domain->id, - 'entitleable_type' => Domain::class + "first_name" => "John", + "last_name" => "Doe", + "currency" => "USD", + "country" => "US" ] ); - $entitlement_mailbox = Entitlement::create( + $user_wallets = $john->wallets()->get(); + + $package_domain = \App\Package::where('title', 'domain-hosting')->first(); + $package_kolab = \App\Package::where('title', 'kolab')->first(); + + $package_domain->assign($domain, $john); + $package_kolab->assign($john); + + $jack = User::create( [ - 'owner_id' => $user->id, - 'wallet_id' => $user_wallets[0]->id, - 'sku_id' => $sku_mailbox->id, - 'entitleable_id' => $user->id, - 'entitleable_type' => User::class + 'name' => "Jack Daniels", + 'email' => 'jack@kolab.org', + 'password' => 'simple123', + 'email_verified_at' => now() ] ); - $user->setSettings( + $jack->setSettings( [ - "first_name" => "John", - "last_name" => "Doe", + "first_name" => "Jack", + "last_name" => "Daniels", "currency" => "USD", "country" => "US" ] ); + + $john->assignPackage($package_kolab, $jack); + + factory(User::class, 50)->create(); } } diff --git a/src/phpstan b/src/phpstan new file mode 120000 --- /dev/null +++ b/src/phpstan @@ -0,0 +1 @@ +../bin/phpstan \ No newline at end of file diff --git a/src/phpstan.neon b/src/phpstan.neon new file mode 100644 --- /dev/null +++ b/src/phpstan.neon @@ -0,0 +1,6 @@ +includes: + - ./vendor/nunomaduro/larastan/extension.neon +parameters: + level: 3 + paths: + - app/ diff --git a/src/phpunit b/src/phpunit new file mode 120000 --- /dev/null +++ b/src/phpunit @@ -0,0 +1 @@ +../bin/phpunit \ No newline at end of file diff --git a/src/phpunit.xml b/src/phpunit.xml --- a/src/phpunit.xml +++ b/src/phpunit.xml @@ -23,6 +23,9 @@ ./app + + + 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 @@ -20,8 +20,16 @@ { parent::setUp(); - User::where('email', 'test1@domainscontroller.com')->delete(); - Domain::where('namespace', 'domainscontroller.com')->delete(); + $this->deleteTestUser('test1@domainscontroller.com'); + $this->deleteTestDomain('domainscontroller.com'); + } + + public function tearDown(): void + { + $this->deleteTestUser('test1@domainscontroller.com'); + $this->deleteTestDomain('domainscontroller.com'); + + parent::tearDown(); } /** 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 @@ -25,7 +25,7 @@ public function tearDown(): void { User::where('email', 'passwordresettest@' . \config('app.domain')) - ->delete(); + ->forceDelete(); } /** 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 @@ -11,7 +11,7 @@ class SignupTest extends TestCase { - private static $domain; + private $domain; /** * {@inheritDoc} @@ -22,8 +22,14 @@ // TODO: Some tests depend on existence of individual and group plans, // we should probably create plans here to not depend on that - $domain = self::getPublicDomain(); - $user = $this->getTestUser("SignupControllerTest1@$domain"); + $this->domain = $this->getPublicDomain(); + + $this->deleteTestUser("SignupControllerTest1@$this->domain"); + $this->deleteTestUser("signuplogin@$this->domain"); + $this->deleteTestUser("admin@external.com"); + + $this->deleteTestDomain('external.com'); + $this->deleteTestDomain('signup-domain.com'); } /** @@ -31,39 +37,37 @@ */ public function tearDown(): void { - $domain = self::getPublicDomain(); + $this->deleteTestUser("SignupControllerTest1@$this->domain"); + $this->deleteTestUser("signuplogin@$this->domain"); + $this->deleteTestUser("admin@external.com"); - User::where('email', "signuplogin@$domain") - ->orWhere('email', "SignupControllerTest1@$domain") - ->orWhere('email', 'admin@external.com') - ->delete(); + $this->deleteTestDomain('external.com'); + $this->deleteTestDomain('signup-domain.com'); - Domain::where('namespace', 'signup-domain.com') - ->orWhere('namespace', 'external.com') - ->delete(); + parent::tearDown(); } /** * Return a public domain for signup tests */ - public function getPublicDomain(): string + private function getPublicDomain(): string { - if (!self::$domain) { + if (!$this->domain) { $this->refreshApplication(); $public_domains = Domain::getPublicDomains(); - self::$domain = reset($public_domains); + $this->domain = reset($public_domains); - if (empty(self::$domain)) { - self::$domain = 'signup-domain.com'; + if (empty($this->domain)) { + $this->domain = 'signup-domain.com'; Domain::create([ - 'namespace' => self::$domain, + 'namespace' => $this->domain, 'status' => Domain::STATUS_ACTIVE, 'type' => Domain::TYPE_PUBLIC, ]); } } - return self::$domain; + return $this->domain; } /** @@ -418,7 +422,7 @@ }); // Check if the code has been removed - $this->assertNull(SignupCode::where($result['code'])->first()); + $this->assertNull(SignupCode::where('code', $result['code'])->first()); // Check if the user has been created $user = User::where('email', $identity)->first(); @@ -451,7 +455,7 @@ 'plan' => 'group', ]; - $response = $this->post('/api/auth/signup/init', $data); + $response = $this->withoutMiddleware()->post('/api/auth/signup/init', $data); $json = $response->json(); $response->assertStatus(200); @@ -603,15 +607,17 @@ ['administrator', $domain, false, ['login' => 'validation.loginexists']], ['sales', $domain, false, ['login' => 'validation.loginexists']], ['root', $domain, false, ['login' => 'validation.loginexists']], + // existing user - ['SignupControllerTest1', $domain, false, ['login' => 'validation.loginexists']], + ['jack', 'kolab.org', true, ['domain' => 'validation.domainexists']], // Domain account ['admin', 'kolabsys.com', true, null], ['testnonsystemdomain', 'invalid', true, ['domain' => 'validation.domaininvalid']], ['testnonsystemdomain', '.com', true, ['domain' => 'validation.domaininvalid']], + // existing user - ['SignupControllerTest1', $domain, true, ['domain' => 'validation.domainexists']], + ['john', 'kolab.org', true, ['domain' => 'validation.domainexists']], ]; } @@ -630,6 +636,6 @@ $result = $method->invoke(new SignupController(), $login, $domain, $external); - $this->assertSame($expected_result, $result); + $this->assertSame($expected_result, $result, var_export(func_get_args(), true)); } } 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 @@ -17,8 +17,16 @@ { parent::setUp(); - User::where('email', 'UsersControllerTest1@userscontroller.com')->delete(); - Domain::where('namespace', 'userscontroller.com')->delete(); + $this->deleteTestUser('UsersControllerTest1@userscontroller.com'); + $this->deleteTestDomain('userscontroller.com'); + } + + public function tearDown(): void + { + $this->deleteTestUser('UsersControllerTest1@userscontroller.com'); + $this->deleteTestDomain('userscontroller.com'); + + parent::tearDown(); } /** diff --git a/src/tests/Feature/DomainOwnerTest.php b/src/tests/Feature/DomainOwnerTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/DomainOwnerTest.php @@ -0,0 +1,44 @@ +forceDelete(); + } + + public function testJohnCreateJane(): void + { + $john = User::where('email', 'john@kolab.org')->first(); + + $jane = User::create( + [ + 'name' => 'Jane Doe', + 'email' => 'jane@kolab.org', + 'password' => 'simple123', + 'email_verified_at' => now() + ] + ); + + $package = Package::where('title', 'kolab')->first(); + + $john->assignPackage($package, $jane); + + // assert jane has a mailbox entitlement + $this->assertTrue($jane->entitlements->count() == 4); + } + + public function tearDown(): void + { + User::where('email', 'jane@kolab.org')->forceDelete(); + parent::tearDown(); + } +} 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 @@ -13,21 +13,28 @@ class DomainTest extends TestCase { + private $domains = [ + 'public-active.com', + 'gmail.com', + 'ci-success-cname.kolab.org', + 'ci-success-txt.kolab.org', + 'ci-failure-cname.kolab.org', + 'ci-failure-txt.kolab.org', + 'ci-failure-none.kolab.org', + ]; + public function setUp(): void { parent::setUp(); - $domains = [ - 'public-active.com', - 'gmail.com', - 'ci-success-cname.kolab.org', - 'ci-success-txt.kolab.org', - 'ci-failure-cname.kolab.org', - 'ci-failure-txt.kolab.org', - 'ci-failure-none.kolab.org', - ]; - - Domain::whereIn('namespace', $domains)->delete(); + Domain::withTrashed()->whereIn('namespace', $this->domains)->forceDelete(); + } + + public function tearDown(): void + { + Domain::whereIn('namespace', $this->domains)->delete(); + + parent::tearDown(); } /** @@ -52,25 +59,9 @@ return $job_domain->id === $domain->id && $job_domain->namespace === $domain->namespace; }); -/* - Queue::assertPushedWithChain(\App\Jobs\DomainCreate::class, [ - \App\Jobs\DomainVerify::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\DomainVerify::class, 1); - Queue::assertPushed(\App\Jobs\DomainVerify::class, function ($job) use ($domain) { - $job_domain = TestCase::getObjectProperty($job, 'domain'); - return $job_domain->id === $domain->id - && $job_domain->namespace === $domain->namespace; - }); -*/ + $job = new \App\Jobs\DomainCreate($domain); + $job->handle(); } /** diff --git a/src/tests/Feature/EntitlementTest.php b/src/tests/Feature/EntitlementTest.php --- a/src/tests/Feature/EntitlementTest.php +++ b/src/tests/Feature/EntitlementTest.php @@ -15,9 +15,16 @@ { parent::setUp(); - User::where('email', 'entitlement-test@kolabnow.com') - ->orWhere('email', 'entitled-user@custom-domain.com') - ->delete(); + $this->deleteTestUser('entitlement-test@kolabnow.com'); + $this->deleteTestUser('entitled-user@custom-domain.com'); + } + + public function tearDown(): void + { + $this->deleteTestUser('entitlement-test@kolabnow.com'); + $this->deleteTestUser('entitled-user@custom-domain.com'); + + parent::tearDown(); } /** @@ -37,16 +44,14 @@ ] ); - $this->assertTrue($owner->id != $user->id); - - $wallets = $owner->wallets()->get(); + $wallet = $owner->wallets()->first(); $entitlement_own_mailbox = new Entitlement( [ 'owner_id' => $owner->id, 'entitleable_id' => $owner->id, 'entitleable_type' => User::class, - 'wallet_id' => $wallets[0]->id, + 'wallet_id' => $wallet->id, 'sku_id' => $sku_mailbox->id, 'description' => "Owner Mailbox Entitlement Test" ] @@ -57,7 +62,7 @@ 'owner_id' => $owner->id, 'entitleable_id' => $domain->id, 'entitleable_type' => Domain::class, - 'wallet_id' => $wallets[0]->id, + 'wallet_id' => $wallet->id, 'sku_id' => $sku_domain->id, 'description' => "User Domain Entitlement Test" ] @@ -68,7 +73,7 @@ 'owner_id' => $owner->id, 'entitleable_id' => $user->id, 'entitleable_type' => User::class, - 'wallet_id' => $wallets[0]->id, + 'wallet_id' => $wallet->id, 'sku_id' => $sku_mailbox->id, 'description' => "User Mailbox Entitlement Test" ] @@ -81,9 +86,43 @@ $this->assertTrue($owner->entitlements()->count() == 3); $this->assertTrue($sku_domain->entitlements()->where('owner_id', $owner->id)->count() == 1); $this->assertTrue($sku_mailbox->entitlements()->where('owner_id', $owner->id)->count() == 2); - $this->assertTrue($wallets[0]->entitlements()->count() == 3); - $this->assertTrue($wallets[0]->fresh()->balance < 0.00); + $this->assertTrue($wallet->entitlements()->count() == 3); + $this->assertTrue($wallet->fresh()->balance < 0.00); + } + + public function testAddExistingEntitlement(): void + { + $this->markTestIncomplete(); + } + + public function testEntitlementFunctions(): void + { + $user = $this->getTestUser('entitlement-test@kolabnow.com'); + + $package = \App\Package::where('title', 'kolab')->first(); + + $user->assignPackage($package); + + $wallet = $user->wallets()->first(); + $this->assertNotNull($wallet); + + $sku = \App\Sku::where('title', 'mailbox')->first(); + $this->assertNotNull($sku); + + $entitlement = Entitlement::where('owner_id', $user->id)->where('sku_id', $sku->id)->first(); + $this->assertNotNull($entitlement); + + $e_sku = $entitlement->sku; + $this->assertSame($sku->id, $e_sku->id); + + $e_wallet = $entitlement->wallet; + $this->assertSame($wallet->id, $e_wallet->id); + + $e_owner = $entitlement->owner; + $this->assertSame($user->id, $e_owner->id); - // TODO: Test case of adding entitlement that already exists + $e_entitleable = $entitlement->entitleable; + $this->assertSame($user->id, $e_entitleable->id); + $this->assertTrue($e_entitleable instanceof \App\User); } } diff --git a/src/tests/Feature/Jobs/DomainCreateTest.php b/src/tests/Feature/Jobs/DomainCreateTest.php --- a/src/tests/Feature/Jobs/DomainCreateTest.php +++ b/src/tests/Feature/Jobs/DomainCreateTest.php @@ -5,6 +5,7 @@ use App\Jobs\DomainCreate; use App\Domain; use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\Queue; use Tests\TestCase; class DomainCreateTest extends TestCase @@ -16,7 +17,14 @@ { parent::setUp(); - Domain::where('namespace', 'domain-create-test.com')->delete(); + $this->deleteTestDomain('domain-create-test.com'); + } + + public function tearDown(): void + { + $this->deleteTestDomain('domain-create-test.com'); + + parent::tearDown(); } /** @@ -24,6 +32,8 @@ */ public function testHandle(): void { + Queue::fake(); + $domain = $this->getTestDomain( 'domain-create-test.com', [ @@ -34,12 +44,6 @@ $this->assertFalse($domain->isLdapReady()); - $mock = \Mockery::mock('alias:App\Backends\LDAP'); - $mock->shouldReceive('createDomain') - ->once() - ->with($domain) - ->andReturn(null); - $job = new DomainCreate($domain); $job->handle(); diff --git a/src/tests/Feature/Jobs/DomainVerifyTest.php b/src/tests/Feature/Jobs/DomainVerifyTest.php --- a/src/tests/Feature/Jobs/DomainVerifyTest.php +++ b/src/tests/Feature/Jobs/DomainVerifyTest.php @@ -18,7 +18,7 @@ Domain::where('namespace', 'gmail.com') ->orWhere('namespace', 'some-non-existing-domain.fff') - ->delete(); + ->forceDelete(); } /** diff --git a/src/tests/Feature/Jobs/PasswordResetEmailTest.php b/src/tests/Feature/Jobs/PasswordResetEmailTest.php --- a/src/tests/Feature/Jobs/PasswordResetEmailTest.php +++ b/src/tests/Feature/Jobs/PasswordResetEmailTest.php @@ -38,7 +38,9 @@ */ public function tearDown(): void { - $this->code->user->delete(); + $this->code->user->forceDelete(); + + parent::tearDown(); } /** diff --git a/src/tests/Feature/Jobs/UserCreateTest.php b/src/tests/Feature/Jobs/UserCreateTest.php --- a/src/tests/Feature/Jobs/UserCreateTest.php +++ b/src/tests/Feature/Jobs/UserCreateTest.php @@ -16,7 +16,7 @@ { parent::setUp(); - User::where('email', 'new-job-user@' . \config('app.domain'))->delete(); + $this->deleteTestUser('new-job-user@' . \config('app.domain')); } /** @@ -28,11 +28,13 @@ $this->assertFalse($user->isLdapReady()); + /* $mock = \Mockery::mock('alias:App\Backends\LDAP'); $mock->shouldReceive('createUser') ->once() ->with($user) ->andReturn(null); + */ $job = new UserCreate($user); $job->handle(); diff --git a/src/tests/Feature/Jobs/UserVerifyTest.php b/src/tests/Feature/Jobs/UserVerifyTest.php --- a/src/tests/Feature/Jobs/UserVerifyTest.php +++ b/src/tests/Feature/Jobs/UserVerifyTest.php @@ -16,7 +16,7 @@ { parent::setUp(); - User::where('email', 'new-job-user@' . \config('app.domain'))->delete(); + User::where('email', 'new-job-user@' . \config('app.domain'))->forceDelete(); } /** @@ -28,24 +28,9 @@ $this->assertFalse($user->isImapReady()); - $mock = \Mockery::mock('alias:App\Backends\IMAP'); - $mock->shouldReceive('verifyAccount') - ->once() - ->with($user->email) - ->andReturn(false); - $job = new UserVerify($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/SkuTest.php b/src/tests/Feature/SkuTest.php --- a/src/tests/Feature/SkuTest.php +++ b/src/tests/Feature/SkuTest.php @@ -5,7 +5,6 @@ use App\Domain; use App\Entitlement; use App\Handlers; -use App\Quota; use App\Sku; use App\User; use Tests\TestCase; @@ -17,8 +16,16 @@ { parent::setUp(); - User::where('email', 'sku-test-user@custom-domain.com')->delete(); - Domain::where('namespace', 'custom-domain.com')->delete(); + $this->deleteTestUser('sku-test-user@custom-domain.com'); + $this->deleteTestDomain('custom-domain.com'); + } + + public function tearDown(): void + { + $this->deleteTestUser('sku-test-user@custom-domain.com'); + $this->deleteTestDomain('custom-domain.com'); + + parent::tearDown(); } /** @@ -35,13 +42,24 @@ ] ); + \Log::debug(var_export($domain->toArray(), true)); + $user = $this->getTestUser('sku-test-user@custom-domain.com'); $wallet = $user->wallets()->first(); // \App\Handlers\Mailbox SKU - // Note, we're testing mailbox SKU before domain SKU as it may potentially fail in that order + // Note, we're testing mailbox SKU before domain SKU as it may potentially fail in that + // order $sku = Sku::where('title', 'mailbox')->first(); - $sku->registerEntitlement($user); + Entitlement::create( + [ + 'owner_id' => $user->id, + 'wallet_id' => $wallet->id, + 'sku_id' => $sku->id, + 'entitleable_id' => $user->id, + 'entitleable_type' => User::class + ] + ); $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); $wallet->refresh(); @@ -50,7 +68,10 @@ $balance = -$sku->cost; $this->assertCount(1, $entitlements); $this->assertEquals($user->id, $entitlements[0]->entitleable_id); - $this->assertSame(Handlers\Mailbox::entitleableClass(), $entitlements[0]->entitleable_type); + $this->assertSame( + Handlers\Mailbox::entitleableClass(), + $entitlements[0]->entitleable_type + ); } else { $this->assertCount(0, $entitlements); } @@ -59,16 +80,40 @@ // \App\Handlers\Domain SKU $sku = Sku::where('title', 'domain')->first(); - $sku->registerEntitlement($user, [$domain]); + Entitlement::create( + [ + 'owner_id' => $user->id, + 'wallet_id' => $wallet->id, + 'sku_id' => $sku->id, + 'entitleable_id' => $domain->id, + 'entitleable_type' => Domain::class + ] + ); + + $entitlements = $sku->entitlements->where('owner_id', $user->id); + + foreach ($entitlements as $entitlement) { + \Log::debug(var_export($entitlement->toArray(), true)); + } - $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); $wallet->refresh(); if ($sku->active) { $balance -= $sku->cost; $this->assertCount(1, $entitlements); - $this->assertEquals($domain->id, $entitlements[0]->entitleable_id); - $this->assertSame(Handlers\Domain::entitleableClass(), $entitlements[0]->entitleable_type); + + $_domain = Domain::find($entitlements->first()->entitleable_id); + + $this->assertEquals( + $domain->id, + $entitlements->first()->entitleable_id, + var_export($_domain->toArray(), true) + ); + + $this->assertSame( + Handlers\Domain::entitleableClass(), + $entitlements->first()->entitleable_type + ); } else { $this->assertCount(0, $entitlements); } @@ -77,16 +122,27 @@ // \App\Handlers\DomainRegistration SKU $sku = Sku::where('title', 'domain-registration')->first(); - $sku->registerEntitlement($user, [$domain]); + Entitlement::create( + [ + 'owner_id' => $user->id, + 'wallet_id' => $user->wallets()->get()[0]->id, + 'sku_id' => $sku->id, + 'entitleable_id' => $domain->id, + 'entitleable_type' => Domain::class + ] + ); - $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); + $entitlements = $sku->entitlements->where('owner_id', $user->id); $wallet->refresh(); if ($sku->active) { $balance -= $sku->cost; $this->assertCount(1, $entitlements); - $this->assertEquals($domain->id, $entitlements[0]->entitleable_id); - $this->assertSame(Handlers\DomainRegistration::entitleableClass(), $entitlements[0]->entitleable_type); + $this->assertEquals($domain->id, $entitlements->first()->entitleable_id); + $this->assertSame( + Handlers\DomainRegistration::entitleableClass(), + $entitlements->first()->entitleable_type + ); } else { $this->assertCount(0, $entitlements); } @@ -95,16 +151,27 @@ // \App\Handlers\DomainHosting SKU $sku = Sku::where('title', 'domain-hosting')->first(); - $sku->registerEntitlement($user, [$domain]); + Entitlement::create( + [ + 'owner_id' => $user->id, + 'wallet_id' => $wallet->id, + 'sku_id' => $sku->id, + 'entitleable_id' => $domain->id, + 'entitleable_type' => Domain::class + ] + ); - $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); + $entitlements = $sku->entitlements->where('owner_id', $user->id); $wallet->refresh(); if ($sku->active) { $balance -= $sku->cost; $this->assertCount(1, $entitlements); - $this->assertEquals($domain->id, $entitlements[0]->entitleable_id); - $this->assertSame(Handlers\DomainHosting::entitleableClass(), $entitlements[0]->entitleable_type); + $this->assertEquals($domain->id, $entitlements->first()->entitleable_id); + $this->assertSame( + Handlers\DomainHosting::entitleableClass(), + $entitlements->first()->entitleable_type + ); } else { $this->assertCount(0, $entitlements); } @@ -113,16 +180,27 @@ // \App\Handlers\Groupware SKU $sku = Sku::where('title', 'groupware')->first(); - $sku->registerEntitlement($user, [$domain]); + Entitlement::create( + [ + 'owner_id' => $user->id, + 'wallet_id' => $user->wallets()->get()[0]->id, + 'sku_id' => $sku->id, + 'entitleable_id' => $user->id, + 'entitleable_type' => User::class + ] + ); - $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); + $entitlements = $sku->entitlements->where('owner_id', $user->id); $wallet->refresh(); if ($sku->active) { $balance -= $sku->cost; $this->assertCount(1, $entitlements); - $this->assertEquals($user->id, $entitlements[0]->entitleable_id); - $this->assertSame(Handlers\Mailbox::entitleableClass(), $entitlements[0]->entitleable_type); + $this->assertEquals($user->id, $entitlements->first()->entitleable_id); + $this->assertSame( + Handlers\Mailbox::entitleableClass(), + $entitlements->first()->entitleable_type + ); } else { $this->assertCount(0, $entitlements); } @@ -131,22 +209,22 @@ // \App\Handlers\Storage SKU $sku = Sku::where('title', 'storage')->first(); - $sku->registerEntitlement($user, [$domain]); + Entitlement::create( + [ + 'owner_id' => $user->id, + 'wallet_id' => $wallet->id, + 'sku_id' => $sku->id, + 'entitleable_id' => $user->id, + 'entitleable_type' => User::class + ] + ); - $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); + $entitlements = $sku->entitlements->where('owner_id', $user->id); $wallet->refresh(); if ($sku->active) { $balance -= $sku->cost; - // For Storage entitlement we expect additional Quota record - $quota = Quota::where('user_id', $user->id)->first(); - $this->assertTrue(!empty($quota)); - // TODO: This should be a constant and/or config option, and probably - // quota should not be in bytes - $this->assertSame(2147483648, $quota->value); $this->assertCount(1, $entitlements); - $this->assertEquals($quota->id, $entitlements[0]->entitleable_id); - $this->assertSame(Handlers\Storage::entitleableClass(), $entitlements[0]->entitleable_type); } else { $this->assertCount(0, $entitlements); } 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 @@ -12,7 +12,20 @@ { parent::setUp(); - User::where('email', 'user-create-test@' . \config('app.domain'))->delete(); + $this->deleteTestUser('user-create-test@' . \config('app.domain')); + $this->deleteTestUser('UserAccountA@UserAccount.com'); + $this->deleteTestUser('UserAccountB@UserAccount.com'); + $this->deleteTestUser('userdeletejob@kolabnow.com'); + } + + public function tearDown(): void + { + $this->deleteTestUser('user-create-test@' . \config('app.domain')); + $this->deleteTestUser('UserAccountA@UserAccount.com'); + $this->deleteTestUser('UserAccountB@UserAccount.com'); + $this->deleteTestUser('userdeletejob@kolabnow.com'); + + parent::tearDown(); } /** @@ -87,6 +100,43 @@ $this->assertContains('kolab.org', $domains); } + public function testUserQuota(): void + { + $user = $this->getTestUser('john@kolab.org'); + $storage_sku = \App\Sku::where('title', 'storage')->first(); + + $count = 0; + + foreach ($user->entitlements()->get() as $entitlement) { + if ($entitlement->sku_id == $storage_sku->id) { + $count += 1; + } + } + + $this->assertTrue($count == 2); + } + + /** + * Test user deletion + */ + public function testUserDelete(): void + { + $user = $this->getTestUser('userdeletejob@kolabnow.com'); + + $package = \App\Package::where('title', 'kolab')->first(); + + $user->assignPackage($package); + + $id = $user->id; + + $user->delete(); + + $job = new \App\Jobs\UserDelete($id); + $job->handle(); + + $user->forceDelete(); + } + /** * Tests for User::findByEmail() */ diff --git a/src/tests/Feature/WalletTest.php b/src/tests/Feature/WalletTest.php --- a/src/tests/Feature/WalletTest.php +++ b/src/tests/Feature/WalletTest.php @@ -27,14 +27,14 @@ parent::setUp(); foreach ($this->users as $user) { - User::where('email', $user)->delete(); + $this->deleteTestUser($user); } } public function tearDown(): void { foreach ($this->users as $user) { - User::where('email', $user)->delete(); + $this->deleteTestUser($user); } } @@ -138,7 +138,10 @@ } ); - $this->assertTrue($userB->accounts()->count() == 1); + $this->assertTrue( + $userB->accounts()->count() == 1, + "number of accounts (1 expected): {$userB->accounts()->count()}" + ); $aWallet = $userA->wallets()->get(); $bAccount = $userB->accounts()->get(); diff --git a/src/tests/TestCase.php b/src/tests/TestCase.php --- a/src/tests/TestCase.php +++ b/src/tests/TestCase.php @@ -11,6 +11,35 @@ { use CreatesApplication; + protected function deleteTestDomain($name) + { + Queue::fake(); + $domain = Domain::withTrashed()->where('namespace', $name)->first(); + if (!$domain) { + return; + } + + $job = new \App\Jobs\DomainDelete($domain); + $job->handle(); + + $domain->forceDelete(); + } + + protected function deleteTestUser($email) + { + Queue::fake(); + $user = User::withTrashed()->where('email', $email)->first(); + + if (!$user) { + return; + } + + $job = new \App\Jobs\UserDelete($user->id); + $job->handle(); + + $user->forceDelete(); + } + /** * Get Domain object by namespace, create it if needed. * Skip LDAP jobs.