diff --git a/bin/quickstart.sh b/bin/quickstart.sh --- a/bin/quickstart.sh +++ b/bin/quickstart.sh @@ -42,10 +42,10 @@ # Always reset .env with .env.example cp src/.env.example src/.env -if [ -f "src/.env.local" ]; then +if [ -f "src/env.local" ]; then # Ensure there's a line ending echo "" >> src/.env - cat src/.env.local >> src/.env + cat src/env.local >> src/.env fi docker pull docker.io/kolab/centos7:latest @@ -97,9 +97,8 @@ ./artisan db:ping --wait php -dmemory_limit=512M ./artisan migrate:refresh --seed ./artisan data:import || : -./artisan swoole:http stop >/dev/null 2>&1 || : -SWOOLE_HTTP_DAEMONIZE=true ./artisan swoole:http start +./artisan octane:stop >/dev/null 2>&1 || : +nohup ./artisan octane:start --host=$(grep OCTANE_HTTP_HOST .env | tail -n1 | sed "s/OCTANE_HTTP_HOST=//") > octane.out & ./artisan horizon:terminate >/dev/null 2>&1 || : -nohup ./artisan horizon >/dev/null 2>&1 & +nohup ./artisan horizon > horizon.out & popd - diff --git a/docker-compose.yml b/docker-compose.yml --- a/docker-compose.yml +++ b/docker-compose.yml @@ -189,7 +189,7 @@ build: context: ./docker/swoole/ container_name: kolab-swoole - image: apheleia/swoole:4.6.x + image: apheleia/swoole:4.8.x worker: build: context: ./docker/worker/ diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile --- a/docker/nginx/Dockerfile +++ b/docker/nginx/Dockerfile @@ -1,4 +1,4 @@ -FROM fedora:34 +FROM fedora:35 MAINTAINER Jeroen van Meeuwen diff --git a/docker/proxy/Dockerfile b/docker/proxy/Dockerfile --- a/docker/proxy/Dockerfile +++ b/docker/proxy/Dockerfile @@ -1,4 +1,4 @@ -FROM fedora:31 +FROM fedora:35 MAINTAINER Jeroen van Meeuwen diff --git a/docker/swoole/Dockerfile b/docker/swoole/Dockerfile --- a/docker/swoole/Dockerfile +++ b/docker/swoole/Dockerfile @@ -1,8 +1,8 @@ -FROM fedora:34 +FROM fedora:35 MAINTAINER Jeroen van Meeuwen -ARG SWOOLE_VERSION=v4.6.7 +ARG SWOOLE_VERSION=v4.8.7 ENV HOME=/opt/app-root/src LABEL io.k8s.description="Platform for serving PHP applications under Swoole" \ @@ -10,6 +10,7 @@ io.openshift.expose-services="8000:http" \ io.openshift.tags="builder,php,swoole" +RUN dnf -y update RUN dnf -y install \ composer \ diffutils \ diff --git a/docker/swoole/rootfs/usr/local/bin/run-container b/docker/swoole/rootfs/usr/local/bin/run-container --- a/docker/swoole/rootfs/usr/local/bin/run-container +++ b/docker/swoole/rootfs/usr/local/bin/run-container @@ -36,7 +36,7 @@ env - exec ./artisan swoole:http start + exec ./artisan octane:start else exec $@ fi diff --git a/src/.env.example b/src/.env.example --- a/src/.env.example +++ b/src/.env.example @@ -30,6 +30,8 @@ LOG_CHANNEL=stack LOG_SLOW_REQUESTS=5 +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug DB_CONNECTION=mysql DB_DATABASE=kolabdev @@ -112,14 +114,7 @@ REDIS_PASSWORD=null REDIS_PORT=6379 -SWOOLE_HOT_RELOAD_ENABLE=true -SWOOLE_HTTP_ACCESS_LOG=true -SWOOLE_HTTP_HOST=127.0.0.1 -SWOOLE_HTTP_PORT=8000 -SWOOLE_HTTP_REACTOR_NUM=1 -SWOOLE_HTTP_WEBSOCKET=true -SWOOLE_HTTP_WORKER_NUM=1 -SWOOLE_OB_OUTPUT=true +OCTANE_HTTP_HOST=127.0.0.1 PAYMENT_PROVIDER= MOLLIE_KEY= @@ -127,7 +122,7 @@ STRIPE_PUBLIC_KEY= STRIPE_WEBHOOK_SECRET= -MAIL_DRIVER=smtp +MAIL_MAILER=smtp MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null @@ -147,6 +142,7 @@ AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false PUSHER_APP_ID= PUSHER_APP_KEY= diff --git a/src/.gitattributes b/src/.gitattributes --- a/src/.gitattributes +++ b/src/.gitattributes @@ -1,5 +1,9 @@ * text=auto -*.css linguist-vendored -*.scss linguist-vendored -*.js linguist-vendored + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + CHANGELOG.md export-ignore diff --git a/src/.styleci.yml b/src/.styleci.yml --- a/src/.styleci.yml +++ b/src/.styleci.yml @@ -1,11 +1,10 @@ php: preset: laravel disabled: - - unused_use + - no_unused_imports finder: not-name: - index.php - - server.php js: finder: not-name: diff --git a/src/app/Auth/LDAPUserProvider.php b/src/app/Auth/LDAPUserProvider.php deleted file mode 100644 --- a/src/app/Auth/LDAPUserProvider.php +++ /dev/null @@ -1,54 +0,0 @@ -get(); - - $count = $entries->count(); - - if ($count == 1) { - return $entries->first(); - } - - if ($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): bool - { - return $user->validateCredentials($credentials['email'], $credentials['password']); - } -} diff --git a/src/app/Auth/SecondFactor.php b/src/app/Auth/SecondFactor.php --- a/src/app/Auth/SecondFactor.php +++ b/src/app/Auth/SecondFactor.php @@ -57,6 +57,7 @@ return; } } + throw new \Exception(\trans('validation.2fainvalid')); } diff --git a/src/app/AuthAttempt.php b/src/app/AuthAttempt.php --- a/src/app/AuthAttempt.php +++ b/src/app/AuthAttempt.php @@ -2,10 +2,10 @@ namespace App; -use Illuminate\Database\Eloquent\Model; -use Iatstuti\Database\Support\NullableFields; use App\Traits\UuidStrKeyTrait; use Carbon\Carbon; +use Dyrynda\Database\Support\NullableFields; +use Illuminate\Database\Eloquent\Model; /** * The eloquent definition of an AuthAttempt. @@ -27,10 +27,10 @@ private const STATUS_ACCEPTED = 'ACCEPTED'; private const STATUS_DENIED = 'DENIED'; - protected $nullable = [ - 'reason', - ]; + /** @var array The attributes that can be not set */ + protected $nullable = ['reason']; + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'ip', 'user_id', @@ -40,6 +40,7 @@ 'last_seen', ]; + /** @var array The attributes that should be cast */ protected $casts = [ 'expires_at' => 'datetime', 'last_seen' => 'datetime' @@ -106,7 +107,7 @@ */ public function notify(): bool { - return \App\CompanionApp::notifyUser($this->user_id, ['token' => $this->id]); + return CompanionApp::notifyUser($this->user_id, ['token' => $this->id]); } /** @@ -154,12 +155,12 @@ * * @return \App\AuthAttempt */ - public static function recordAuthAttempt(\App\User $user, $clientIP) + public static function recordAuthAttempt(User $user, $clientIP) { - $authAttempt = \App\AuthAttempt::where('ip', $clientIP)->where('user_id', $user->id)->first(); + $authAttempt = AuthAttempt::where('ip', $clientIP)->where('user_id', $user->id)->first(); if (!$authAttempt) { - $authAttempt = new \App\AuthAttempt(); + $authAttempt = new AuthAttempt(); $authAttempt->ip = $clientIP; $authAttempt->user_id = $user->id; } 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 @@ -1356,7 +1356,7 @@ */ private static function throwException($ldap, string $message): void { - if (empty(self::$ldap) && !empty($ldap)) { + if (empty(self::$ldap)) { $ldap->close(); } diff --git a/src/app/CompanionApp.php b/src/app/CompanionApp.php --- a/src/app/CompanionApp.php +++ b/src/app/CompanionApp.php @@ -11,6 +11,7 @@ */ class CompanionApp extends Model { + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'name', 'user_id', @@ -67,7 +68,7 @@ */ public static function notifyUser($userId, $data): bool { - $notificationTokens = \App\CompanionApp::where('user_id', $userId) + $notificationTokens = CompanionApp::where('user_id', $userId) ->where('mfa_enabled', true) ->pluck('notification_token') ->all(); diff --git a/src/app/Console/Command.php b/src/app/Console/Command.php --- a/src/app/Console/Command.php +++ b/src/app/Console/Command.php @@ -9,9 +9,9 @@ /** * This needs to be here to be used. * - * @var null + * @var string */ - protected $commandPrefix = null; + protected $commandPrefix = ''; /** * Annotate this command as being dangerous for any potential unintended consequences. diff --git a/src/app/Console/Commands/Data/Import/LdifCommand.php b/src/app/Console/Commands/Data/Import/LdifCommand.php --- a/src/app/Console/Commands/Data/Import/LdifCommand.php +++ b/src/app/Console/Commands/Data/Import/LdifCommand.php @@ -121,6 +121,7 @@ $lastAttr = null; $insertFunc = function ($limit = 0) use (&$entry, &$inserts) { + // @phpstan-ignore-next-line if (!empty($entry)) { if ($entry = $this->parseLDAPEntry($entry)) { $inserts[] = $entry; diff --git a/src/app/Console/Kernel.php b/src/app/Console/Kernel.php --- a/src/app/Console/Kernel.php +++ b/src/app/Console/Kernel.php @@ -8,15 +8,6 @@ class Kernel extends ConsoleKernel { /** - * The Artisan commands provided by your application. - * - * @var array - */ - protected $commands = [ - // - ]; - - /** * Define the application's command schedule. * * @param Schedule $schedule The application's command schedule diff --git a/src/app/Console/ObjectCommand.php b/src/app/Console/ObjectCommand.php --- a/src/app/Console/ObjectCommand.php +++ b/src/app/Console/ObjectCommand.php @@ -13,7 +13,7 @@ * * @var string */ - protected $commandPrefix = null; + protected $commandPrefix = ''; /** * The object class that we are operating on, for example \App\User::class diff --git a/src/app/Console/ObjectCreateCommand.php b/src/app/Console/ObjectCreateCommand.php --- a/src/app/Console/ObjectCreateCommand.php +++ b/src/app/Console/ObjectCreateCommand.php @@ -61,7 +61,5 @@ } else { $this->error("Object could not be created."); } - - return $object; } } diff --git a/src/app/Discount.php b/src/app/Discount.php --- a/src/app/Discount.php +++ b/src/app/Discount.php @@ -26,17 +26,12 @@ 'discount' => 'integer', ]; - protected $fillable = [ - 'active', - 'code', - 'description', - 'discount', - ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['active', 'code', 'description', 'discount']; + + /** @var array Translatable properties */ + public $translatable = ['description']; - /** @var array Translatable properties */ - public $translatable = [ - 'description', - ]; /** * Discount value mutator @@ -67,6 +62,6 @@ */ public function wallets() { - return $this->hasMany('App\Wallet'); + return $this->hasMany(Wallet::class); } } diff --git a/src/app/Domain.php b/src/app/Domain.php --- a/src/app/Domain.php +++ b/src/app/Domain.php @@ -2,7 +2,6 @@ namespace App; -use App\Wallet; use App\Traits\BelongsToTenantTrait; use App\Traits\DomainConfigTrait; use App\Traits\EntitleableTrait; @@ -56,12 +55,16 @@ public const HASH_TEXT = 2; public const HASH_CNAME = 3; - protected $fillable = [ - 'namespace', - 'status', - 'type' + /** @var array The attributes that should be cast */ + protected $casts = [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'deleted_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['namespace', 'status', 'type']; + /** * Assign a package to a domain. The domain should not belong to any existing entitlements. * @@ -97,8 +100,8 @@ public static function getPublicDomains(): array { return self::withEnvTenantContext() - ->whereRaw(sprintf('(type & %s)', Domain::TYPE_PUBLIC)) - ->get(['namespace'])->pluck('namespace')->toArray(); + ->where('type', '&', Domain::TYPE_PUBLIC) + ->pluck('namespace')->all(); } /** @@ -315,11 +318,11 @@ $suffixLen = strlen($suffix); return !( - \App\User::whereRaw('substr(email, ?) = ?', [-$suffixLen, $suffix])->exists() - || \App\UserAlias::whereRaw('substr(alias, ?) = ?', [-$suffixLen, $suffix])->exists() - || \App\Group::whereRaw('substr(email, ?) = ?', [-$suffixLen, $suffix])->exists() - || \App\Resource::whereRaw('substr(email, ?) = ?', [-$suffixLen, $suffix])->exists() - || \App\SharedFolder::whereRaw('substr(email, ?) = ?', [-$suffixLen, $suffix])->exists() + User::whereRaw('substr(email, ?) = ?', [-$suffixLen, $suffix])->exists() + || UserAlias::whereRaw('substr(alias, ?) = ?', [-$suffixLen, $suffix])->exists() + || Group::whereRaw('substr(email, ?) = ?', [-$suffixLen, $suffix])->exists() + || Resource::whereRaw('substr(email, ?) = ?', [-$suffixLen, $suffix])->exists() + || SharedFolder::whereRaw('substr(email, ?) = ?', [-$suffixLen, $suffix])->exists() ); } @@ -370,7 +373,7 @@ return []; } - $mailboxSKU = \App\Sku::withObjectTenantContext($this)->where('title', 'mailbox')->first(); + $mailboxSKU = Sku::withObjectTenantContext($this)->where('title', 'mailbox')->first(); if (!$mailboxSKU) { \Log::error("No mailbox SKU available."); @@ -378,7 +381,7 @@ } return $wallet->entitlements() - ->where('entitleable_type', \App\User::class) + ->where('entitleable_type', User::class) ->where('sku_id', $mailboxSKU->id) ->get() ->pluck('entitleable') diff --git a/src/app/DomainSetting.php b/src/app/DomainSetting.php --- a/src/app/DomainSetting.php +++ b/src/app/DomainSetting.php @@ -14,9 +14,8 @@ */ class DomainSetting extends Model { - protected $fillable = [ - 'domain_id', 'key', 'value' - ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['domain_id', 'key', 'value']; /** * The domain to which this setting belongs. @@ -26,7 +25,7 @@ public function domain() { return $this->belongsTo( - '\App\Domain', + Domain::class, 'domain_id', /* local */ 'id' /* remote */ ); diff --git a/src/app/Entitlement.php b/src/app/Entitlement.php --- a/src/app/Entitlement.php +++ b/src/app/Entitlement.php @@ -2,9 +2,9 @@ namespace App; +use App\Traits\UuidStrKeyTrait; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -use App\Traits\UuidStrKeyTrait; /** * The eloquent definition of an Entitlement. @@ -29,11 +29,7 @@ use SoftDeletes; use UuidStrKeyTrait; - /** - * The fillable columns for this Entitlement - * - * @var array - */ + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'sku_id', 'wallet_id', @@ -44,6 +40,7 @@ 'fee', ]; + /** @var array The attributes that should be cast */ protected $casts = [ 'cost' => 'integer', 'fee' => 'integer' @@ -80,10 +77,10 @@ */ public function createTransaction($type, $amount = null) { - $transaction = \App\Transaction::create( + $transaction = Transaction::create( [ 'object_id' => $this->id, - 'object_type' => \App\Entitlement::class, + 'object_type' => Entitlement::class, 'type' => $type, 'amount' => $amount ] @@ -110,7 +107,7 @@ */ public function entitleableTitle(): ?string { - if ($this->entitleable instanceof \App\Domain) { + if ($this->entitleable instanceof Domain) { return $this->entitleable->namespace; } @@ -151,7 +148,7 @@ */ public function sku() { - return $this->belongsTo('App\Sku'); + return $this->belongsTo(Sku::class); } /** @@ -161,7 +158,7 @@ */ public function wallet() { - return $this->belongsTo('App\Wallet'); + return $this->belongsTo(Wallet::class); } /** diff --git a/src/app/Exceptions/Handler.php b/src/app/Exceptions/Handler.php --- a/src/app/Exceptions/Handler.php +++ b/src/app/Exceptions/Handler.php @@ -2,61 +2,37 @@ namespace App\Exceptions; -use Exception; use Illuminate\Auth\AuthenticationException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Support\Facades\DB; class Handler extends ExceptionHandler { - /** - * A list of the exception types that are not reported. - * - * @var array - */ + /** @var string[] A list of the exception types that are not reported */ protected $dontReport = [ \Laravel\Passport\Exceptions\OAuthServerException::class, \League\OAuth2\Server\Exception\OAuthServerException::class ]; - /** - * A list of the inputs that are never flashed for validation exceptions. - * - * @var array - */ + /** @var string[] A list of the inputs that are never flashed for validation exceptions */ protected $dontFlash = [ + 'current_password', 'password', 'password_confirmation', ]; - /** - * Report or log an exception. - * - * @param \Exception $exception - * - * @return void - */ - public function report(Exception $exception) - { - parent::report($exception); - } /** - * Render an exception into an HTTP response. - * - * @param \Illuminate\Http\Request $request - * @param \Exception $exception - * - * @return \Symfony\Component\HttpFoundation\Response + * Register the exception handling callbacks for the application. */ - public function render($request, Exception $exception) + public function register() { - // Rollback uncommitted transactions - while (DB::transactionLevel() > 0) { - DB::rollBack(); - } - - return parent::render($request, $exception); + $this->reportable(function (\Throwable $e) { + // Rollback uncommitted transactions + while (DB::transactionLevel() > 0) { + DB::rollBack(); + } + }); } /** diff --git a/src/app/Group.php b/src/app/Group.php --- a/src/app/Group.php +++ b/src/app/Group.php @@ -9,7 +9,6 @@ use App\Traits\SettingsTrait; use App\Traits\StatusPropertyTrait; use App\Traits\UuidIntKeyTrait; -use App\Wallet; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; @@ -45,6 +44,14 @@ // group has been created in LDAP public const STATUS_LDAP_READY = 1 << 4; + /** @var array The attributes that should be cast */ + protected $casts = [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'deleted_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', + ]; + + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'email', 'members', diff --git a/src/app/GroupSetting.php b/src/app/GroupSetting.php --- a/src/app/GroupSetting.php +++ b/src/app/GroupSetting.php @@ -14,9 +14,8 @@ */ class GroupSetting extends Model { - protected $fillable = [ - 'group_id', 'key', 'value' - ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['group_id', 'key', 'value']; /** * The group to which this setting belongs. @@ -25,6 +24,6 @@ */ public function group() { - return $this->belongsTo(\App\Group::class, 'group_id', 'id'); + return $this->belongsTo(Group::class, 'group_id', 'id'); } } 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 @@ -16,10 +16,6 @@ */ class PasswordResetController extends Controller { - /** @var \App\VerificationCode A verification code object */ - protected $code; - - /** * Sends password reset code to the user's external email * @@ -98,7 +94,7 @@ // For last-step remember the code object, so we can delete it // with single SQL query (->delete()) instead of two (::destroy()) - $this->code = $code; + $request->code = $code; return response()->json([ 'status' => 'success', @@ -121,7 +117,7 @@ return $v; } - $user = $this->code->user; + $user = $request->code->user; // Validate the password $v = Validator::make( @@ -138,7 +134,7 @@ $user->save(); // Remove the verification code - $this->code->delete(); + $request->code->delete(); return AuthController::logonResponse($user, $request->password); } 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 @@ -25,13 +25,6 @@ */ class SignupController extends Controller { - /** @var ?\App\SignupCode A verification code object */ - protected $code; - - /** @var ?\App\Plan Signup plan object */ - protected $plan; - - /** * Returns plans definitions for signup. * @@ -177,7 +170,7 @@ // For signup last-step mode remember the code object, so we can delete it // with single SQL query (->delete()) instead of two (::destroy()) - $this->code = $code; + $request->code = $code; $has_domain = $this->getPlan()->hasDomain(); @@ -328,8 +321,8 @@ } // Remove the verification code - if ($this->code) { - $this->code->delete(); + if ($request->code) { + $request->code->delete(); } DB::commit(); @@ -344,10 +337,12 @@ */ protected function getPlan() { - if (!$this->plan) { + $request = request(); + + if (!$request->plan || !$request->plan instanceof Plan) { // Get the plan if specified and exists... - if ($this->code && $this->code->plan) { - $plan = Plan::withEnvTenantContext()->where('title', $this->code->plan)->first(); + if ($request->code && $request->code->plan) { + $plan = Plan::withEnvTenantContext()->where('title', $request->code->plan)->first(); } // ...otherwise use the default plan @@ -356,10 +351,10 @@ $plan = Plan::withEnvTenantContext()->where('title', 'individual')->first(); } - $this->plan = $plan; + $request->plan = $plan; } - return $this->plan; + return $request->plan; } /** diff --git a/src/app/Http/Controllers/API/V4/Admin/UsersController.php b/src/app/Http/Controllers/API/V4/Admin/UsersController.php --- a/src/app/Http/Controllers/API/V4/Admin/UsersController.php +++ b/src/app/Http/Controllers/API/V4/Admin/UsersController.php @@ -215,6 +215,8 @@ } $user->assignSku($sku); + + /** @var \App\Entitlement $entitlement */ $entitlement = $user->entitlements()->where('sku_id', $sku->id)->first(); return response()->json([ diff --git a/src/app/Http/Controllers/API/V4/AuthAttemptsController.php b/src/app/Http/Controllers/API/V4/AuthAttemptsController.php --- a/src/app/Http/Controllers/API/V4/AuthAttemptsController.php +++ b/src/app/Http/Controllers/API/V4/AuthAttemptsController.php @@ -9,7 +9,6 @@ class AuthAttemptsController extends Controller { - /** * Confirm the authentication attempt. * diff --git a/src/app/Http/Controllers/API/V4/WalletsController.php b/src/app/Http/Controllers/API/V4/WalletsController.php --- a/src/app/Http/Controllers/API/V4/WalletsController.php +++ b/src/app/Http/Controllers/API/V4/WalletsController.php @@ -161,6 +161,7 @@ // Get sub-transactions for the specified transaction ID, first // check access rights to the transaction's wallet + /** @var ?\App\Transaction $transaction */ $transaction = $wallet->transactions()->where('id', $transaction)->first(); if (!$transaction) { diff --git a/src/app/Http/Kernel.php b/src/app/Http/Kernel.php --- a/src/app/Http/Kernel.php +++ b/src/app/Http/Kernel.php @@ -11,12 +11,13 @@ * * These middleware are run during every request to your application. * - * @var array + * @var array */ protected $middleware = [ + // \App\Http\Middleware\TrustHosts::class, \App\Http\Middleware\RequestLogger::class, \App\Http\Middleware\TrustProxies::class, - \App\Http\Middleware\CheckForMaintenanceMode::class, + \App\Http\Middleware\PreventRequestsDuringMaintenance::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, @@ -31,7 +32,7 @@ /** * The application's route middleware groups. * - * @var array + * @var array */ protected $middlewareGroups = [ 'web' => [ @@ -45,8 +46,8 @@ ], 'api' => [ - //'throttle:120,1', - 'bindings', + // 'throttle:api', + \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ]; @@ -55,13 +56,12 @@ * * These middleware may be assigned to groups or used individually. * - * @var array + * @var array */ protected $routeMiddleware = [ 'admin' => \App\Http\Middleware\AuthenticateAdmin::class, 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, - 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, @@ -72,25 +72,6 @@ ]; /** - * The priority-sorted list of middleware. - * - * This forces non-global middleware to always be in the given order. - * - * @var array - */ - protected $middlewarePriority = [ - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \App\Http\Middleware\AuthenticateAdmin::class, - \App\Http\Middleware\AuthenticateReseller::class, - \App\Http\Middleware\Authenticate::class, - \Illuminate\Session\Middleware\AuthenticateSession::class, - \Illuminate\Routing\Middleware\SubstituteBindings::class, - \Illuminate\Auth\Middleware\Authorize::class, - \App\Http\Middleware\ContentSecurityPolicy::class, - ]; - - /** * Handle an incoming HTTP request. * * @param \Illuminate\Http\Request $request HTTP Request object diff --git a/src/app/Http/Middleware/EncryptCookies.php b/src/app/Http/Middleware/EncryptCookies.php --- a/src/app/Http/Middleware/EncryptCookies.php +++ b/src/app/Http/Middleware/EncryptCookies.php @@ -9,7 +9,7 @@ /** * The names of the cookies that should not be encrypted. * - * @var array + * @var array */ protected $except = [ // diff --git a/src/app/Http/Middleware/CheckForMaintenanceMode.php b/src/app/Http/Middleware/PreventRequestsDuringMaintenance.php rename from src/app/Http/Middleware/CheckForMaintenanceMode.php rename to src/app/Http/Middleware/PreventRequestsDuringMaintenance.php --- a/src/app/Http/Middleware/CheckForMaintenanceMode.php +++ b/src/app/Http/Middleware/PreventRequestsDuringMaintenance.php @@ -2,14 +2,14 @@ namespace App\Http\Middleware; -use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware; +use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware; -class CheckForMaintenanceMode extends Middleware +class PreventRequestsDuringMaintenance extends Middleware { /** * The URIs that should be reachable while maintenance mode is enabled. * - * @var array + * @var array */ protected $except = [ // diff --git a/src/app/Http/Middleware/RedirectIfAuthenticated.php b/src/app/Http/Middleware/RedirectIfAuthenticated.php --- a/src/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/src/app/Http/Middleware/RedirectIfAuthenticated.php @@ -3,6 +3,7 @@ namespace App\Http\Middleware; use Closure; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class RedirectIfAuthenticated @@ -12,14 +13,18 @@ * * @param \Illuminate\Http\Request $request * @param \Closure $next - * @param string|null $guard + * @param string|null ...$guards * * @return mixed */ - public function handle($request, Closure $next, $guard = null) + public function handle(Request $request, Closure $next, ...$guards) { - if (Auth::guard($guard)->check()) { - return redirect('/dashboard'); + $guards = empty($guards) ? [null] : $guards; + + foreach ($guards as $guard) { + if (Auth::guard($guard)->check()) { + return redirect('/dashboard'); + } } return $next($request); diff --git a/src/app/Http/Middleware/TrustHosts.php b/src/app/Http/Middleware/TrustHosts.php new file mode 100644 --- /dev/null +++ b/src/app/Http/Middleware/TrustHosts.php @@ -0,0 +1,20 @@ + + */ + public function hosts() + { + return [ + $this->allSubdomainsOfApplicationUrl(), + ]; + } +} diff --git a/src/app/Http/Middleware/TrustProxies.php b/src/app/Http/Middleware/TrustProxies.php --- a/src/app/Http/Middleware/TrustProxies.php +++ b/src/app/Http/Middleware/TrustProxies.php @@ -2,15 +2,15 @@ namespace App\Http\Middleware; +use Illuminate\Http\Middleware\TrustProxies as Middleware; use Illuminate\Http\Request; -use Fideloper\Proxy\TrustProxies as Middleware; class TrustProxies extends Middleware { /** * The trusted proxies for this application. * - * @var array|string + * @var array|string|null */ protected $proxies = [ '10.0.0.0/8', @@ -24,5 +24,9 @@ * * @var int */ - protected $headers = Request::HEADER_X_FORWARDED_ALL; + protected $headers = Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_AWS_ELB; } diff --git a/src/app/Http/Middleware/VerifyCsrfToken.php b/src/app/Http/Middleware/VerifyCsrfToken.php --- a/src/app/Http/Middleware/VerifyCsrfToken.php +++ b/src/app/Http/Middleware/VerifyCsrfToken.php @@ -7,16 +7,9 @@ class VerifyCsrfToken extends Middleware { /** - * Indicates whether the XSRF-TOKEN cookie should be set on the response. - * - * @var bool - */ - protected $addHttpCookie = true; - - /** * The URIs that should be excluded from CSRF verification. * - * @var array + * @var array */ protected $except = [ // diff --git a/src/app/IP4Net.php b/src/app/IP4Net.php --- a/src/app/IP4Net.php +++ b/src/app/IP4Net.php @@ -9,6 +9,7 @@ { protected $table = "ip4nets"; + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'rir_name', 'net_number', diff --git a/src/app/IP6Net.php b/src/app/IP6Net.php --- a/src/app/IP6Net.php +++ b/src/app/IP6Net.php @@ -9,6 +9,7 @@ { protected $table = "ip6nets"; + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'rir_name', 'net_number', diff --git a/src/app/Jobs/PaymentEmail.php b/src/app/Jobs/PaymentEmail.php --- a/src/app/Jobs/PaymentEmail.php +++ b/src/app/Jobs/PaymentEmail.php @@ -22,7 +22,7 @@ public $tries = 2; /** @var int The number of seconds to wait before retrying the job. */ - public $retryAfter = 10; + public $backoff = 10; /** @var bool Delete the job if the wallet no longer exist. */ public $deleteWhenMissingModels = true; @@ -30,7 +30,7 @@ /** @var \App\Payment A payment object */ protected $payment; - /** @var \App\User A wallet controller */ + /** @var ?\App\User A wallet controller */ protected $controller; diff --git a/src/app/Jobs/PaymentMandateDisabledEmail.php b/src/app/Jobs/PaymentMandateDisabledEmail.php --- a/src/app/Jobs/PaymentMandateDisabledEmail.php +++ b/src/app/Jobs/PaymentMandateDisabledEmail.php @@ -22,7 +22,7 @@ public $tries = 2; /** @var int The number of seconds to wait before retrying the job. */ - public $retryAfter = 10; + public $backoff = 10; /** @var bool Delete the job if the wallet no longer exist. */ public $deleteWhenMissingModels = true; @@ -30,7 +30,7 @@ /** @var \App\Wallet A wallet object */ protected $wallet; - /** @var \App\User A wallet controller */ + /** @var ?\App\User A wallet controller */ protected $controller; diff --git a/src/app/Jobs/SignupInvitationEmail.php b/src/app/Jobs/SignupInvitationEmail.php --- a/src/app/Jobs/SignupInvitationEmail.php +++ b/src/app/Jobs/SignupInvitationEmail.php @@ -24,7 +24,7 @@ public $deleteWhenMissingModels = true; /** @var int The number of seconds to wait before retrying the job. */ - public $retryAfter = 10; + public $backoff = 10; /** @var SignupInvitation Signup invitation object */ protected $invitation; diff --git a/src/app/Jobs/WalletCharge.php b/src/app/Jobs/WalletCharge.php --- a/src/app/Jobs/WalletCharge.php +++ b/src/app/Jobs/WalletCharge.php @@ -21,7 +21,7 @@ protected $wallet; /** @var int The number of seconds to wait before retrying the job. */ - public $retryAfter = 10; + public $backoff = 10; /** @var int How many times retry the job if it fails. */ public $tries = 5; diff --git a/src/app/Jobs/WalletCheck.php b/src/app/Jobs/WalletCheck.php --- a/src/app/Jobs/WalletCheck.php +++ b/src/app/Jobs/WalletCheck.php @@ -30,7 +30,7 @@ public const THRESHOLD_INITIAL = 'initial'; /** @var int The number of seconds to wait before retrying the job. */ - public $retryAfter = 10; + public $backoff = 10; /** @var int How many times retry the job if it fails. */ public $tries = 5; diff --git a/src/app/Observers/OpenVidu/ConnectionObserver.php b/src/app/Observers/OpenVidu/ConnectionObserver.php --- a/src/app/Observers/OpenVidu/ConnectionObserver.php +++ b/src/app/Observers/OpenVidu/ConnectionObserver.php @@ -34,7 +34,7 @@ foreach ($keys as $key => $type) { $newState = $connection->metadata[$key] ?? null; - $oldState = $this->getOriginal($connection, 'metadata')[$key] ?? null; + $oldState = $connection->getOriginal('metadata')[$key] ?? null; if ($newState !== $oldState) { $params[$key] = $type == 'bool' ? !empty($newState) : $newState; @@ -47,25 +47,4 @@ $connection->room->signal('connectionUpdate', $params); } } - - /** - * A wrapper to getOriginal() on an object - * - * @param \App\OpenVidu\Connection $connection The connection. - * @param string $property The property name - * - * @return mixed - */ - private function getOriginal($connection, $property) - { - $original = $connection->getOriginal($property); - - // The original value for a property is in a format stored in database - // I.e. for 'metadata' it is a JSON string instead of an array - if ($property == 'metadata') { - $original = json_decode($original, true); - } - - return $original; - } } diff --git a/src/app/OpenVidu/Room.php b/src/app/OpenVidu/Room.php --- a/src/app/OpenVidu/Room.php +++ b/src/app/OpenVidu/Room.php @@ -27,9 +27,9 @@ public const REQUEST_ACCEPTED = 'accepted'; public const REQUEST_DENIED = 'denied'; - private const OV_ROLE_MODERATOR = 'MODERATOR'; + private const OV_ROLE_MODERATOR = 'MODERATOR'; // @phpstan-ignore-line private const OV_ROLE_PUBLISHER = 'PUBLISHER'; - private const OV_ROLE_SUBSCRIBER = 'SUBSCRIBER'; + private const OV_ROLE_SUBSCRIBER = 'SUBSCRIBER'; // @phpstan-ignore-line protected $fillable = [ 'user_id', diff --git a/src/app/Package.php b/src/app/Package.php --- a/src/app/Package.php +++ b/src/app/Package.php @@ -39,6 +39,7 @@ public $timestamps = false; + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'description', 'discount_rate', @@ -46,7 +47,7 @@ 'title', ]; - /** @var array Translatable properties */ + /** @var array Translatable properties */ public $translatable = [ 'name', 'description', @@ -83,7 +84,7 @@ public function isDomain(): bool { foreach ($this->skus as $sku) { - if ($sku->handler_class::entitleableClass() == \App\Domain::class) { + if ($sku->handler_class::entitleableClass() == Domain::class) { return true; } } @@ -98,11 +99,8 @@ */ public function skus() { - return $this->belongsToMany( - 'App\Sku', - 'package_skus' - )->using('App\PackageSku')->withPivot( - ['qty'] - ); + return $this->belongsToMany(Sku::class, 'package_skus') + ->using(PackageSku::class) + ->withPivot(['qty']); } } diff --git a/src/app/PackageSku.php b/src/app/PackageSku.php --- a/src/app/PackageSku.php +++ b/src/app/PackageSku.php @@ -16,6 +16,7 @@ */ class PackageSku extends Pivot { + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'package_id', 'sku_id', @@ -23,6 +24,7 @@ 'qty' ]; + /** @var array The attributes that should be cast */ protected $casts = [ 'cost' => 'integer', 'qty' => 'integer' @@ -71,7 +73,7 @@ */ public function package() { - return $this->belongsTo('App\Package'); + return $this->belongsTo(Package::class); } /** @@ -81,6 +83,6 @@ */ public function sku() { - return $this->belongsTo('App\Sku'); + return $this->belongsTo(Sku::class); } } diff --git a/src/app/Payment.php b/src/app/Payment.php --- a/src/app/Payment.php +++ b/src/app/Payment.php @@ -20,10 +20,12 @@ public $incrementing = false; protected $keyType = 'string'; + /** @var array The attributes that should be cast */ protected $casts = [ 'amount' => 'integer' ]; + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'id', 'wallet_id', @@ -52,6 +54,6 @@ */ public function wallet() { - return $this->belongsTo('\App\Wallet', 'wallet_id', 'id'); + return $this->belongsTo(Wallet::class, 'wallet_id', 'id'); } } diff --git a/src/app/Plan.php b/src/app/Plan.php --- a/src/app/Plan.php +++ b/src/app/Plan.php @@ -34,6 +34,7 @@ public $timestamps = false; + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'title', 'name', @@ -47,14 +48,15 @@ 'discount_rate', ]; + /** @var array The attributes that should be cast */ protected $casts = [ - 'promo_from' => 'datetime', - 'promo_to' => 'datetime', + 'promo_from' => 'datetime:Y-m-d H:i:s', + 'promo_to' => 'datetime:Y-m-d H:i:s', 'discount_qty' => 'integer', 'discount_rate' => 'integer' ]; - /** @var array Translatable properties */ + /** @var array Translatable properties */ public $translatable = [ 'name', 'description', @@ -87,18 +89,15 @@ */ public function packages() { - return $this->belongsToMany( - 'App\Package', - 'plan_packages' - )->using('App\PlanPackage')->withPivot( - [ - 'qty', - 'qty_min', - 'qty_max', - 'discount_qty', - 'discount_rate' - ] - ); + return $this->belongsToMany(Package::class, 'plan_packages') + ->using(PlanPackage::class) + ->withPivot([ + 'qty', + 'qty_min', + 'qty_max', + 'discount_qty', + 'discount_rate' + ]); } /** diff --git a/src/app/PlanPackage.php b/src/app/PlanPackage.php --- a/src/app/PlanPackage.php +++ b/src/app/PlanPackage.php @@ -19,6 +19,7 @@ */ class PlanPackage extends Pivot { + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'plan_id', 'package_id', @@ -29,6 +30,7 @@ 'discount_rate' ]; + /** @var array The attributes that should be cast */ protected $casts = [ 'qty' => 'integer', 'qty_max' => 'integer', @@ -62,7 +64,7 @@ */ public function package() { - return $this->belongsTo('App\Package'); + return $this->belongsTo(Package::class); } /** @@ -72,6 +74,6 @@ */ public function plan() { - return $this->belongsTo('App\Plan'); + return $this->belongsTo(Plan::class); } } diff --git a/src/app/Providers/AuthServiceProvider.php b/src/app/Providers/AuthServiceProvider.php --- a/src/app/Providers/AuthServiceProvider.php +++ b/src/app/Providers/AuthServiceProvider.php @@ -2,9 +2,8 @@ namespace App\Providers; -use App\Auth\LDAPUserProvider; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Gate; +use Illuminate\Support\Facades\Route; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Laravel\Passport\Passport; @@ -13,7 +12,7 @@ /** * The policy mappings for the application. * - * @var array + * @var array */ protected $policies = [ // 'App\Model' => 'App\Policies\ModelPolicy', @@ -28,13 +27,6 @@ { $this->registerPolicies(); - Auth::provider( - 'ldap', - function ($app, array $config) { - return new LDAPUserProvider($app['hash'], $config['model']); - } - ); - // Hashes all secrets and thus makes them non-recoverable /* Passport::hashClientSecrets(); */ // Only enable routes for access tokens @@ -43,7 +35,7 @@ $router->forAccessTokens(); // Override the default route to avoid rate-limiting. - \Route::post('/token', [ + Route::post('/token', [ 'uses' => 'AccessTokenController@issueToken', 'as' => 'passport.token', ]); diff --git a/src/app/Providers/EventServiceProvider.php b/src/app/Providers/EventServiceProvider.php --- a/src/app/Providers/EventServiceProvider.php +++ b/src/app/Providers/EventServiceProvider.php @@ -12,7 +12,7 @@ /** * The event listener mappings for the application. * - * @var array + * @var array> */ protected $listen = [ Registered::class => [ @@ -27,8 +27,16 @@ */ public function boot() { - parent::boot(); - // } + + /** + * Determine if events and listeners should be automatically discovered. + * + * @return bool + */ + public function shouldDiscoverEvents() + { + return false; + } } diff --git a/src/app/Providers/PassportServiceProvider.php b/src/app/Providers/PassportServiceProvider.php --- a/src/app/Providers/PassportServiceProvider.php +++ b/src/app/Providers/PassportServiceProvider.php @@ -10,7 +10,6 @@ class PassportServiceProvider extends \Laravel\Passport\PassportServiceProvider { - /** * Make the authorization service instance. * diff --git a/src/app/Providers/RouteServiceProvider.php b/src/app/Providers/RouteServiceProvider.php --- a/src/app/Providers/RouteServiceProvider.php +++ b/src/app/Providers/RouteServiceProvider.php @@ -2,73 +2,43 @@ namespace App\Providers; -use Illuminate\Support\Facades\Route; +use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\RateLimiter; +use Illuminate\Support\Facades\Route; class RouteServiceProvider extends ServiceProvider { /** - * This namespace is applied to your controller routes. - * - * In addition, it is set as the URL generator's root namespace. - * - * @var string - */ - protected $namespace = 'App\Http\Controllers'; - - /** * Define your route model bindings, pattern filters, etc. * * @return void */ public function boot() { - // + $this->configureRateLimiting(); - parent::boot(); - } + $this->routes(function () { + $prefix = \trim(\parse_url(\config('app.url'), PHP_URL_PATH), '/') . '/'; - /** - * Define the routes for the application. - * - * @return void - */ - public function map() - { - $this->mapApiRoutes(); - - $this->mapWebRoutes(); + Route::prefix($prefix . 'api') + ->group(base_path('routes/api.php')); - // + Route::middleware('web') + ->group(base_path('routes/web.php')); + }); } /** - * Define the "web" routes for the application. - * - * These routes all receive session state, CSRF protection, etc. - * - * @return void - */ - protected function mapWebRoutes() - { - Route::middleware('web') - ->namespace($this->namespace) - ->group(base_path('routes/web.php')); - } - - /** - * Define the "api" routes for the application. - * - * These routes are typically stateless. + * Configure the rate limiters for the application. * * @return void */ - protected function mapApiRoutes() + protected function configureRateLimiting() { - // Note: We removed the prefix from here, to have more control - // over it in routes/api.php - Route::middleware('api') - ->namespace($this->namespace) - ->group(base_path('routes/api.php')); + RateLimiter::for('api', function (Request $request) { + return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); + }); } } diff --git a/src/app/Resource.php b/src/app/Resource.php --- a/src/app/Resource.php +++ b/src/app/Resource.php @@ -9,7 +9,6 @@ use App\Traits\SettingsTrait; use App\Traits\StatusPropertyTrait; use App\Traits\UuidIntKeyTrait; -use App\Wallet; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; @@ -49,9 +48,13 @@ // A template for the email attribute on a resource creation public const EMAIL_TEMPLATE = 'resource-{id}@{domainName}'; - protected $fillable = [ - 'email', - 'name', - 'status', + /** @var array The attributes that should be cast */ + protected $casts = [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'deleted_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', ]; + + /** @var array The attributes that are mass assignable */ + protected $fillable = ['email', 'name', 'status']; } diff --git a/src/app/ResourceSetting.php b/src/app/ResourceSetting.php --- a/src/app/ResourceSetting.php +++ b/src/app/ResourceSetting.php @@ -14,9 +14,8 @@ */ class ResourceSetting extends Model { - protected $fillable = [ - 'resource_id', 'key', 'value' - ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['resource_id', 'key', 'value']; /** * The resource to which this setting belongs. @@ -25,6 +24,6 @@ */ public function resource() { - return $this->belongsTo(\App\Resource::class, 'resource_id', 'id'); + return $this->belongsTo(Resource::class, 'resource_id', 'id'); } } diff --git a/src/app/SharedFolder.php b/src/app/SharedFolder.php --- a/src/app/SharedFolder.php +++ b/src/app/SharedFolder.php @@ -54,7 +54,14 @@ /** @const string A template for the email attribute on a folder creation */ public const EMAIL_TEMPLATE = '{type}-{id}@{domainName}'; - /** @var array Mass-assignable properties */ + /** @var array The attributes that should be cast */ + protected $casts = [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'deleted_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', + ]; + + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'email', 'name', diff --git a/src/app/SharedFolderAlias.php b/src/app/SharedFolderAlias.php --- a/src/app/SharedFolderAlias.php +++ b/src/app/SharedFolderAlias.php @@ -34,6 +34,6 @@ */ public function sharedFolder() { - return $this->belongsTo('\App\SharedFolder', 'shared_folder_id', 'id'); + return $this->belongsTo(SharedFolder::class, 'shared_folder_id', 'id'); } } diff --git a/src/app/SharedFolderSetting.php b/src/app/SharedFolderSetting.php --- a/src/app/SharedFolderSetting.php +++ b/src/app/SharedFolderSetting.php @@ -14,9 +14,8 @@ */ class SharedFolderSetting extends Model { - protected $fillable = [ - 'shared_folder_id', 'key', 'value' - ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['shared_folder_id', 'key', 'value']; /** * The folder to which this setting belongs. @@ -25,6 +24,6 @@ */ public function folder() { - return $this->belongsTo(\App\SharedFolder::class, 'shared_folder_id', 'id'); + return $this->belongsTo(SharedFolder::class, 'shared_folder_id', 'id'); } } diff --git a/src/app/SignupCode.php b/src/app/SignupCode.php --- a/src/app/SignupCode.php +++ b/src/app/SignupCode.php @@ -35,32 +35,16 @@ public const CODE_EXP_HOURS = 24; - /** - * The primary key associated with the table. - * - * @var string - */ + /** @var string The primary key associated with the table */ protected $primaryKey = 'code'; - /** - * Indicates if the IDs are auto-incrementing. - * - * @var bool - */ + /** @var bool Indicates if the IDs are auto-incrementing */ public $incrementing = false; - /** - * The "type" of the auto-incrementing ID. - * - * @var string - */ + /** @var string The "type" of the auto-incrementing ID */ protected $keyType = 'string'; - /** - * The attributes that are mass assignable. - * - * @var array - */ + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'code', 'email', @@ -72,14 +56,12 @@ 'voucher' ]; - protected $casts = ['headers' => 'array']; + /** @var array The attributes that should be cast */ + protected $casts = [ + 'expires_at' => 'datetime:Y-m-d H:i:s', + 'headers' => 'array' + ]; - /** - * The attributes that should be mutated to dates. - * - * @var array - */ - protected $dates = ['expires_at']; /** * Check if code is expired. diff --git a/src/app/SignupInvitation.php b/src/app/SignupInvitation.php --- a/src/app/SignupInvitation.php +++ b/src/app/SignupInvitation.php @@ -31,13 +31,10 @@ public const STATUS_COMPLETED = 1 << 3; - /** - * The attributes that are mass assignable. - * - * @var array - */ + /** @var array The attributes that are mass assignable */ protected $fillable = ['email']; + /** * Returns whether this invitation process completed (user signed up) * @@ -85,6 +82,6 @@ */ public function user() { - return $this->belongsTo('App\User', 'user_id', 'id'); + return $this->belongsTo(User::class, 'user_id', 'id'); } } diff --git a/src/app/Sku.php b/src/app/Sku.php --- a/src/app/Sku.php +++ b/src/app/Sku.php @@ -2,10 +2,10 @@ namespace App; -use Illuminate\Database\Eloquent\Model; -use Spatie\Translatable\HasTranslations; use App\Traits\BelongsToTenantTrait; use App\Traits\UuidStrKeyTrait; +use Illuminate\Database\Eloquent\Model; +use Spatie\Translatable\HasTranslations; /** * The eloquent definition of a Stock Keeping Unit (SKU). @@ -28,10 +28,12 @@ use HasTranslations; use UuidStrKeyTrait; + /** @var array The attributes that should be cast */ protected $casts = [ 'units_free' => 'integer' ]; + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'active', 'cost', @@ -45,7 +47,7 @@ 'units_free', ]; - /** @var array Translatable properties */ + /** @var array Translatable properties */ public $translatable = [ 'name', 'description', @@ -58,7 +60,7 @@ */ public function entitlements() { - return $this->hasMany('App\Entitlement'); + return $this->hasMany(Entitlement::class); } /** @@ -68,9 +70,8 @@ */ public function packages() { - return $this->belongsToMany( - 'App\Package', - 'package_skus' - )->using('App\PackageSku')->withPivot(['cost', 'qty']); + return $this->belongsToMany(Package::class, 'package_skus') + ->using(PackageSku::class) + ->withPivot(['cost', 'qty']); } } diff --git a/src/app/Tenant.php b/src/app/Tenant.php --- a/src/app/Tenant.php +++ b/src/app/Tenant.php @@ -15,10 +15,8 @@ { use SettingsTrait; - protected $fillable = [ - 'id', - 'title', - ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['id', 'title']; /** @@ -67,7 +65,7 @@ */ public function discounts() { - return $this->hasMany('App\Discount'); + return $this->hasMany(Discount::class); } /** @@ -77,7 +75,7 @@ */ public function signupInvitations() { - return $this->hasMany('App\SignupInvitation'); + return $this->hasMany(SignupInvitation::class); } /* @@ -87,7 +85,7 @@ */ public function wallet(): ?Wallet { - $user = \App\User::where('role', 'reseller')->where('tenant_id', $this->id)->first(); + $user = User::where('role', 'reseller')->where('tenant_id', $this->id)->first(); return $user ? $user->wallets->first() : null; } diff --git a/src/app/TenantSetting.php b/src/app/TenantSetting.php --- a/src/app/TenantSetting.php +++ b/src/app/TenantSetting.php @@ -14,9 +14,8 @@ */ class TenantSetting extends Model { - protected $fillable = [ - 'tenant_id', 'key', 'value' - ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['tenant_id', 'key', 'value']; /** * The tenant to which this setting belongs. @@ -25,6 +24,6 @@ */ public function tenant() { - return $this->belongsTo('\App\Tenant', 'tenant_id', 'id'); + return $this->belongsTo(Tenant::class, 'tenant_id', 'id'); } } diff --git a/src/app/Traits/EmailPropertyTrait.php b/src/app/Traits/EmailPropertyTrait.php --- a/src/app/Traits/EmailPropertyTrait.php +++ b/src/app/Traits/EmailPropertyTrait.php @@ -61,7 +61,7 @@ * @param string $email Email address * @param bool $return_object Return model instance instead of a boolean * - * @return object|bool True or Model object if found, False otherwise + * @return static|bool True or Model object if found, False otherwise */ public static function emailExists(string $email, bool $return_object = false) { diff --git a/src/app/Transaction.php b/src/app/Transaction.php --- a/src/app/Transaction.php +++ b/src/app/Transaction.php @@ -2,8 +2,6 @@ namespace App; -use App\Entitlement; -use App\Wallet; use App\Traits\UuidStrKeyTrait; use Illuminate\Database\Eloquent\Model; @@ -34,27 +32,23 @@ public const WALLET_REFUND = 'refund'; public const WALLET_CHARGEBACK = 'chback'; + /** @var array The attributes that are mass assignable */ protected $fillable = [ // actor, if any 'user_email', - // entitlement, wallet 'object_id', 'object_type', - // entitlement: created, deleted, billed // wallet: debit, credit, award, penalty 'type', - 'amount', - 'description', - // parent, for example wallet debit is parent for entitlements charged. 'transaction_id' ]; - /** @var array Casts properties as type */ + /** @var array Casts properties as type */ protected $casts = [ 'amount' => 'integer', ]; diff --git a/src/app/User.php b/src/app/User.php --- a/src/app/User.php +++ b/src/app/User.php @@ -10,11 +10,11 @@ use App\Traits\UuidIntKeyTrait; use App\Traits\SettingsTrait; use App\Traits\StatusPropertyTrait; +use Dyrynda\Database\Support\NullableFields; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Foundation\Auth\User as Authenticatable; -use Iatstuti\Database\Support\NullableFields; use Laravel\Passport\HasApiTokens; use League\OAuth2\Server\Exception\OAuthServerException; @@ -57,11 +57,7 @@ // user in "limited feature-set" state public const STATUS_DEGRADED = 1 << 6; - /** - * The attributes that are mass assignable. - * - * @var array - */ + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'id', 'email', @@ -70,22 +66,26 @@ 'status', ]; - /** - * The attributes that should be hidden for arrays. - * - * @var array - */ + /** @var array The attributes that should be hidden for arrays */ protected $hidden = [ 'password', 'password_ldap', 'role' ]; + /** @var array The attributes that can be null */ protected $nullable = [ 'password', 'password_ldap' ]; + /** @var array The attributes that should be cast */ + protected $casts = [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'deleted_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', + ]; + /** * Any wallets on which this user is a controller. * @@ -96,7 +96,7 @@ public function accounts() { return $this->belongsToMany( - 'App\Wallet', // The foreign object definition + Wallet::class, // The foreign object definition 'user_accounts', // The table name 'user_id', // The local foreign key 'wallet_id' // The remote foreign key @@ -274,8 +274,8 @@ $query->withEnvTenantContext(); } - $query->whereRaw(sprintf('(domains.type & %s)', Domain::TYPE_PUBLIC)) - ->whereRaw(sprintf('(domains.status & %s)', Domain::STATUS_ACTIVE)); + $query->where('domains.type', '&', Domain::TYPE_PUBLIC) + ->where('domains.status', '&', Domain::STATUS_ACTIVE); }); } @@ -337,7 +337,7 @@ return $user; } - $aliases = \App\UserAlias::where('alias', $email)->get(); + $aliases = UserAlias::where('alias', $email)->get(); if (count($aliases) == 1) { return $aliases->first()->user; @@ -395,7 +395,7 @@ $name = trim($settings['first_name'] . ' ' . $settings['last_name']); if (empty($name) && $fallback) { - return trim(\trans('app.siteuser', ['site' => \App\Tenant::getConfig($this->tenant_id, 'app.name')])); + return trim(\trans('app.siteuser', ['site' => Tenant::getConfig($this->tenant_id, 'app.name')])); } return $name; @@ -408,7 +408,7 @@ */ public function passwords() { - return $this->hasMany('App\UserPassword'); + return $this->hasMany(UserPassword::class); } /** @@ -421,7 +421,7 @@ */ public function resources($with_accounts = true) { - return $this->entitleables(\App\Resource::class, $with_accounts); + return $this->entitleables(Resource::class, $with_accounts); } /** @@ -434,7 +434,7 @@ */ public function sharedFolders($with_accounts = true) { - return $this->entitleables(\App\SharedFolder::class, $with_accounts); + return $this->entitleables(SharedFolder::class, $with_accounts); } public function senderPolicyFrameworkWhitelist($clientName) @@ -512,7 +512,7 @@ */ public function verificationcodes() { - return $this->hasMany('App\VerificationCode', 'user_id', 'id'); + return $this->hasMany(VerificationCode::class, 'user_id', 'id'); } /** @@ -522,7 +522,7 @@ */ public function wallets() { - return $this->hasMany('App\Wallet'); + return $this->hasMany(Wallet::class); } /** diff --git a/src/app/UserAlias.php b/src/app/UserAlias.php --- a/src/app/UserAlias.php +++ b/src/app/UserAlias.php @@ -13,9 +13,8 @@ */ class UserAlias extends Model { - protected $fillable = [ - 'user_id', 'alias' - ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['user_id', 'alias']; /** * Ensure the email address is appropriately cased. @@ -34,6 +33,6 @@ */ public function user() { - return $this->belongsTo('\App\User', 'user_id', 'id'); + return $this->belongsTo(User::class, 'user_id', 'id'); } } diff --git a/src/app/UserPassword.php b/src/app/UserPassword.php --- a/src/app/UserPassword.php +++ b/src/app/UserPassword.php @@ -16,13 +16,15 @@ /** @var bool Indicates if the model should be timestamped. */ public $timestamps = false; - /** @var array The attributes that should be mutated to dates. */ - protected $dates = ['created_at']; + /** @var array The attributes that should be cast. */ + protected $casts = [ + 'created_at' => 'datetime:Y-m-d H:i:s', + ]; - /** @var array The attributes that are mass assignable. */ + /** @var array The attributes that are mass assignable. */ protected $fillable = ['user_id', 'password']; - /** @var array The attributes that should be hidden for arrays. */ + /** @var array The attributes that should be hidden for arrays. */ protected $hidden = ['password']; /** @@ -32,6 +34,6 @@ */ public function user() { - return $this->belongsTo('\App\User', 'user_id', 'id'); + return $this->belongsTo(User::class, 'user_id', 'id'); } } diff --git a/src/app/UserSetting.php b/src/app/UserSetting.php --- a/src/app/UserSetting.php +++ b/src/app/UserSetting.php @@ -14,9 +14,8 @@ */ class UserSetting extends Model { - protected $fillable = [ - 'user_id', 'key', 'value' - ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['user_id', 'key', 'value']; /** * The user to which this setting belongs. @@ -25,6 +24,6 @@ */ public function user() { - return $this->belongsTo('\App\User', 'user_id', 'id'); + return $this->belongsTo(User::class, 'user_id', 'id'); } } diff --git a/src/app/VerificationCode.php b/src/app/VerificationCode.php --- a/src/app/VerificationCode.php +++ b/src/app/VerificationCode.php @@ -26,49 +26,25 @@ // Code expires after so many hours public const CODE_EXP_HOURS = 8; - /** - * The primary key associated with the table. - * - * @var string - */ + /** @var string The primary key associated with the table */ protected $primaryKey = 'code'; - /** - * Indicates if the IDs are auto-incrementing. - * - * @var bool - */ + /** @var bool Indicates if the IDs are auto-incrementing */ public $incrementing = false; - /** - * The "type" of the auto-incrementing ID. - * - * @var string - */ + /** @var string The "type" of the auto-incrementing ID */ protected $keyType = 'string'; - /** - * Indicates if the model should be timestamped. - * - * @var bool - */ + /** @var bool Indicates if the model should be timestamped */ public $timestamps = false; - /** - * Casts properties as type - * - * @var array - */ + /** @var array Casts properties as type */ protected $casts = [ 'active' => 'boolean', 'expires_at' => 'datetime', ]; - /** - * The attributes that are mass assignable. - * - * @var array - */ + /** @var array The attributes that are mass assignable */ protected $fillable = ['user_id', 'code', 'short_code', 'mode', 'expires_at', 'active']; @@ -102,6 +78,6 @@ */ public function user() { - return $this->belongsTo('\App\User', 'user_id', 'id'); + return $this->belongsTo(User::class, 'user_id', 'id'); } } diff --git a/src/app/Wallet.php b/src/app/Wallet.php --- a/src/app/Wallet.php +++ b/src/app/Wallet.php @@ -2,11 +2,10 @@ namespace App; -use App\User; use App\Traits\SettingsTrait; use App\Traits\UuidStrKeyTrait; use Carbon\Carbon; -use Iatstuti\Database\Support\NullableFields; +use Dyrynda\Database\Support\NullableFields; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\DB; @@ -30,39 +29,23 @@ public $timestamps = false; - /** - * The attributes' default values. - * - * @var array - */ + /** @var array The attributes' default values */ protected $attributes = [ 'balance' => 0, ]; - /** - * The attributes that are mass assignable. - * - * @var array - */ + /** @var array The attributes that are mass assignable */ protected $fillable = [ 'currency', 'description' ]; - /** - * The attributes that can be not set. - * - * @var array - */ + /** @var array The attributes that can be not set */ protected $nullable = [ 'description', ]; - /** - * The types of attributes to which its values will be cast - * - * @var array - */ + /** @var array The types of attributes to which its values will be cast */ protected $casts = [ 'balance' => 'integer', ]; @@ -160,7 +143,7 @@ } $entitlementTransactions[] = $entitlement->createTransaction( - \App\Transaction::ENTITLEMENT_BILLED, + Transaction::ENTITLEMENT_BILLED, $cost ); } @@ -232,8 +215,8 @@ public function controllers() { return $this->belongsToMany( - 'App\User', // The foreign object definition - 'user_accounts', // The table name + User::class, // The foreign object definition + 'user_accounts', // The table name 'wallet_id', // The local foreign key 'user_id' // The remote foreign key ); @@ -269,11 +252,11 @@ $this->save(); - \App\Transaction::create( + Transaction::create( [ 'object_id' => $this->id, - 'object_type' => \App\Wallet::class, - 'type' => \App\Transaction::WALLET_CREDIT, + 'object_type' => Wallet::class, + 'type' => Transaction::WALLET_CREDIT, 'amount' => $amount, 'description' => $description ] @@ -301,18 +284,18 @@ $this->save(); - $transaction = \App\Transaction::create( + $transaction = Transaction::create( [ 'object_id' => $this->id, - 'object_type' => \App\Wallet::class, - 'type' => \App\Transaction::WALLET_DEBIT, + 'object_type' => Wallet::class, + 'type' => Transaction::WALLET_DEBIT, 'amount' => $amount * -1, 'description' => $description ] ); if (!empty($eTIDs)) { - \App\Transaction::whereIn('id', $eTIDs)->update(['transaction_id' => $transaction->id]); + Transaction::whereIn('id', $eTIDs)->update(['transaction_id' => $transaction->id]); } return $this; @@ -325,7 +308,7 @@ */ public function discount() { - return $this->belongsTo('App\Discount', 'discount_id', 'id'); + return $this->belongsTo(Discount::class, 'discount_id', 'id'); } /** @@ -335,7 +318,7 @@ */ public function entitlements() { - return $this->hasMany('App\Entitlement'); + return $this->hasMany(Entitlement::class); } /** @@ -396,7 +379,7 @@ */ public function owner() { - return $this->belongsTo('App\User', 'user_id', 'id'); + return $this->belongsTo(User::class, 'user_id', 'id'); } /** @@ -406,7 +389,7 @@ */ public function payments() { - return $this->hasMany('App\Payment'); + return $this->hasMany(Payment::class); } /** @@ -430,10 +413,10 @@ */ public function transactions() { - return \App\Transaction::where( + return Transaction::where( [ 'object_id' => $this->id, - 'object_type' => \App\Wallet::class + 'object_type' => Wallet::class ] ); } @@ -492,7 +475,7 @@ // FIXME: Shouldn't we store also cost=0 transactions (to have the full history)? $entitlementTransactions[] = $entitlement->createTransaction( - \App\Transaction::ENTITLEMENT_BILLED, + Transaction::ENTITLEMENT_BILLED, $cost ); } diff --git a/src/app/WalletSetting.php b/src/app/WalletSetting.php --- a/src/app/WalletSetting.php +++ b/src/app/WalletSetting.php @@ -14,9 +14,8 @@ */ class WalletSetting extends Model { - protected $fillable = [ - 'wallet_id', 'key', 'value' - ]; + /** @var array The attributes that are mass assignable */ + protected $fillable = ['wallet_id', 'key', 'value']; /** * The wallet to which this setting belongs. @@ -25,6 +24,6 @@ */ public function wallet() { - return $this->belongsTo('\App\Wallet', 'wallet_id', 'id'); + return $this->belongsTo(Wallet::class, 'wallet_id', 'id'); } } diff --git a/src/composer.json b/src/composer.json --- a/src/composer.json +++ b/src/composer.json @@ -1,7 +1,7 @@ { - "name": "laravel/laravel", + "name": "kolab/kolab4", "type": "project", - "description": "The Laravel Framework.", + "description": "Kolab 4", "keywords": [ "framework", "laravel" @@ -14,37 +14,33 @@ } ], "require": { - "php": "^7.3", - "barryvdh/laravel-dompdf": "^0.8.6", - "doctrine/dbal": "^2.13", - "dyrynda/laravel-nullable-fields": "*", - "fideloper/proxy": "^4.0", - "guzzlehttp/guzzle": "^7.3", + "php": "^8.0", + "barryvdh/laravel-dompdf": "^1.0.0", + "doctrine/dbal": "^3.3.2", + "dyrynda/laravel-nullable-fields": "^4.2.0", + "guzzlehttp/guzzle": "^7.4.1", "kolab/net_ldap3": "dev-master", - "laravel/framework": "6.*", - "laravel/horizon": "^3", - "laravel/passport": "^9", - "laravel/tinker": "^2.4", - "mlocati/spf-lib": "^3.0", - "mollie/laravel-mollie": "^2.9", + "laravel/framework": "^9.2", + "laravel/horizon": "^5.9", + "laravel/octane": "^1.2", + "laravel/passport": "^10.3", + "laravel/tinker": "^2.7", + "mlocati/spf-lib": "^3.1", + "mollie/laravel-mollie": "^2.19", "moontoast/math": "^1.2", - "morrislaptop/laravel-queue-clear": "^1.2", "pear/crypt_gpg": "^1.6.6", - "silviolleite/laravelpwa": "^2.0", - "spatie/laravel-translatable": "^4.2", - "spomky-labs/otphp": "~4.0.0", - "stripe/stripe-php": "^7.29", - "swooletw/laravel-swoole": "^2.6" + "predis/predis": "^1.1.10", + "spatie/laravel-translatable": "^5.2", + "spomky-labs/otphp": "~10.0.0", + "stripe/stripe-php": "^7.29" }, "require-dev": { - "beyondcode/laravel-er-diagram-generator": "^1.3", - "code-lts/doctum": "^5.1", - "kirschbaum-development/mail-intercept": "^0.2.4", - "laravel/dusk": "~6.15.0", - "nunomaduro/larastan": "^0.7", - "phpstan/phpstan": "^0.12", + "code-lts/doctum": "^5.5.1", + "laravel/dusk": "~6.22.0", + "nunomaduro/larastan": "^2.0", + "phpstan/phpstan": "^1.4", "phpunit/phpunit": "^9", - "squizlabs/php_codesniffer": "3.*" + "squizlabs/php_codesniffer": "^3.6" }, "config": { "optimize-autoloader": true, @@ -77,6 +73,9 @@ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "@php artisan package:discover --ansi" ], + "post-update-cmd": [ + "@php artisan vendor:publish --tag=laravel-assets --ansi --force" + ], "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], diff --git a/src/config/app.php b/src/config/app.php --- a/src/config/app.php +++ b/src/config/app.php @@ -57,7 +57,7 @@ 'public_url' => env('APP_PUBLIC_URL', env('APP_URL', 'http://localhost')), - 'asset_url' => env('ASSET_URL', null), + 'asset_url' => env('ASSET_URL'), 'support_url' => env('SUPPORT_URL', null), @@ -215,44 +215,9 @@ | */ - 'aliases' => [ - 'App' => Illuminate\Support\Facades\App::class, - 'Arr' => Illuminate\Support\Arr::class, - 'Artisan' => Illuminate\Support\Facades\Artisan::class, - 'Auth' => Illuminate\Support\Facades\Auth::class, - 'Blade' => Illuminate\Support\Facades\Blade::class, - 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, - 'Bus' => Illuminate\Support\Facades\Bus::class, - 'Cache' => Illuminate\Support\Facades\Cache::class, - 'Config' => Illuminate\Support\Facades\Config::class, - 'Cookie' => Illuminate\Support\Facades\Cookie::class, - 'Crypt' => Illuminate\Support\Facades\Crypt::class, - 'DB' => Illuminate\Support\Facades\DB::class, - 'Eloquent' => Illuminate\Database\Eloquent\Model::class, - 'Event' => Illuminate\Support\Facades\Event::class, - 'File' => Illuminate\Support\Facades\File::class, - 'Gate' => Illuminate\Support\Facades\Gate::class, - 'Hash' => Illuminate\Support\Facades\Hash::class, - 'Lang' => Illuminate\Support\Facades\Lang::class, - 'Log' => Illuminate\Support\Facades\Log::class, - 'Mail' => Illuminate\Support\Facades\Mail::class, - 'Notification' => Illuminate\Support\Facades\Notification::class, - 'Password' => Illuminate\Support\Facades\Password::class, + 'aliases' => \Illuminate\Support\Facades\Facade::defaultAliases()->merge([ 'PDF' => Barryvdh\DomPDF\Facade::class, - 'Queue' => Illuminate\Support\Facades\Queue::class, - 'Redirect' => Illuminate\Support\Facades\Redirect::class, - 'Redis' => Illuminate\Support\Facades\Redis::class, - 'Request' => Illuminate\Support\Facades\Request::class, - 'Response' => Illuminate\Support\Facades\Response::class, - 'Route' => Illuminate\Support\Facades\Route::class, - 'Schema' => Illuminate\Support\Facades\Schema::class, - 'Session' => Illuminate\Support\Facades\Session::class, - 'Storage' => Illuminate\Support\Facades\Storage::class, - 'Str' => Illuminate\Support\Str::class, - 'URL' => Illuminate\Support\Facades\URL::class, - 'Validator' => Illuminate\Support\Facades\Validator::class, - 'View' => Illuminate\Support\Facades\View::class, - ], + ])->toArray(), 'headers' => [ 'csp' => env('APP_HEADER_CSP', ""), diff --git a/src/config/auth.php b/src/config/auth.php --- a/src/config/auth.php +++ b/src/config/auth.php @@ -66,7 +66,7 @@ 'providers' => [ 'users' => [ - 'driver' => 'ldap', + 'driver' => 'eloquent', 'model' => App\User::class, ], diff --git a/src/config/broadcasting.php b/src/config/broadcasting.php --- a/src/config/broadcasting.php +++ b/src/config/broadcasting.php @@ -11,7 +11,7 @@ | framework when an event needs to be broadcast. You may set this to | any of the connections defined in the "connections" array below. | - | Supported: "pusher", "redis", "log", "null" + | Supported: "pusher", "ably", "redis", "log", "null" | */ @@ -39,6 +39,14 @@ 'cluster' => env('PUSHER_APP_CLUSTER'), 'useTLS' => true, ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + + 'ably' => [ + 'driver' => 'ably', + 'key' => env('ABLY_KEY'), ], 'redis' => [ diff --git a/src/config/cache.php b/src/config/cache.php --- a/src/config/cache.php +++ b/src/config/cache.php @@ -39,12 +39,14 @@ 'array' => [ 'driver' => 'array', + 'serialize' => false, ], 'database' => [ 'driver' => 'database', 'table' => 'cache', 'connection' => null, + 'lock_connection' => null, ], 'file' => [ @@ -74,6 +76,7 @@ 'redis' => [ 'driver' => 'redis', 'connection' => 'cache', + 'lock_connection' => 'default', ], 'dynamodb' => [ @@ -85,6 +88,10 @@ 'endpoint' => env('DYNAMODB_ENDPOINT'), ], + 'octane' => [ + 'driver' => 'octane', + ], + ], /* @@ -98,6 +105,6 @@ | */ - 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'), + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache_'), ]; diff --git a/src/config/database.php b/src/config/database.php --- a/src/config/database.php +++ b/src/config/database.php @@ -75,7 +75,7 @@ 'charset' => 'utf8', 'prefix' => '', 'prefix_indexes' => true, - 'schema' => 'public', + 'search_path' => 'public', 'sslmode' => 'prefer', ], @@ -135,7 +135,7 @@ 'default' => [ 'url' => env('REDIS_URL'), 'host' => env('REDIS_HOST', '127.0.0.1'), - 'password' => env('REDIS_PASSWORD', null), + 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', 6379), 'database' => env('REDIS_DB', 0), ], @@ -143,7 +143,7 @@ 'cache' => [ 'url' => env('REDIS_URL'), 'host' => env('REDIS_HOST', '127.0.0.1'), - 'password' => env('REDIS_PASSWORD', null), + 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', 6379), 'database' => env('REDIS_CACHE_DB', 1), ], diff --git a/src/config/filesystems.php b/src/config/filesystems.php --- a/src/config/filesystems.php +++ b/src/config/filesystems.php @@ -13,20 +13,7 @@ | */ - 'default' => env('FILESYSTEM_DRIVER', 'local'), - - /* - |-------------------------------------------------------------------------- - | Default Cloud Filesystem Disk - |-------------------------------------------------------------------------- - | - | Many applications store files both locally and in the cloud. For this - | reason, you may specify a default "cloud" driver here. This driver - | will be bound as the Cloud disk implementation in the container. - | - */ - - 'cloud' => env('FILESYSTEM_CLOUD', 's3'), + 'default' => env('FILESYSTEM_DISK', 'local'), /* |-------------------------------------------------------------------------- @@ -37,7 +24,7 @@ | may even configure multiple disks of the same driver. Defaults have | been setup for each driver as an example of the required options. | - | Supported Drivers: "local", "ftp", "sftp", "s3", "rackspace" + | Supported Drivers: "local", "ftp", "sftp", "s3" | */ @@ -56,7 +43,7 @@ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', + 'url' => env('APP_URL') . '/storage', 'visibility' => 'public', ], @@ -67,8 +54,25 @@ 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), ], ], + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + ]; diff --git a/src/config/hashing.php b/src/config/hashing.php --- a/src/config/hashing.php +++ b/src/config/hashing.php @@ -44,9 +44,9 @@ */ 'argon' => [ - 'memory' => 1024, - 'threads' => 2, - 'time' => 2, + 'memory' => 65536, + 'threads' => 1, + 'time' => 4, ], ]; diff --git a/src/config/horizon.php b/src/config/horizon.php --- a/src/config/horizon.php +++ b/src/config/horizon.php @@ -147,7 +147,7 @@ 'queue' => ['default'], 'balance' => 'auto', 'maxProcesses' => 1, - 'minProcesses' => 0, + 'minProcesses' => 1, 'tries' => 1, ], ], @@ -158,7 +158,7 @@ 'queue' => ['default'], 'balance' => 'auto', 'maxProcesses' => 1, - 'minProcesses' => 0, + 'minProcesses' => 1, 'tries' => 1, ], ], diff --git a/src/config/logging.php b/src/config/logging.php --- a/src/config/logging.php +++ b/src/config/logging.php @@ -1,5 +1,6 @@ env('LOG_DEPRECATIONS_CHANNEL', 'null'), + + /* + |-------------------------------------------------------------------------- | Log Channels |-------------------------------------------------------------------------- | @@ -43,13 +57,13 @@ 'single' => [ 'driver' => 'single', 'path' => storage_path('logs/laravel.log'), - 'level' => 'debug', + 'level' => env('LOG_LEVEL', 'debug'), ], 'daily' => [ 'driver' => 'daily', 'path' => storage_path('logs/laravel.log'), - 'level' => 'debug', + 'level' => env('LOG_LEVEL', 'debug'), 'days' => 14, ], @@ -58,21 +72,23 @@ 'url' => env('LOG_SLACK_WEBHOOK_URL'), 'username' => 'Laravel Log', 'emoji' => ':boom:', - 'level' => 'critical', + 'level' => env('LOG_LEVEL', 'critical'), ], 'papertrail' => [ 'driver' => 'monolog', - 'level' => 'debug', - 'handler' => SyslogUdpHandler::class, + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), 'handler_with' => [ 'host' => env('PAPERTRAIL_URL'), 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://' . env('PAPERTRAIL_URL') . ':' . env('PAPERTRAIL_PORT'), ], ], 'stderr' => [ 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), 'handler' => StreamHandler::class, 'formatter' => env('LOG_STDERR_FORMATTER'), 'with' => [ @@ -82,15 +98,22 @@ 'syslog' => [ 'driver' => 'syslog', - 'level' => 'debug', + 'level' => env('LOG_LEVEL', 'debug'), ], 'errorlog' => [ 'driver' => 'errorlog', - 'level' => 'debug', + 'level' => env('LOG_LEVEL', 'debug'), + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, ], - ], - 'slow_log' => (float) env('LOG_SLOW_REQUESTS', 5), + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + ], ]; diff --git a/src/config/mail.php b/src/config/mail.php --- a/src/config/mail.php +++ b/src/config/mail.php @@ -4,45 +4,80 @@ /* |-------------------------------------------------------------------------- - | Mail Driver + | Default Mailer |-------------------------------------------------------------------------- | - | Laravel supports both SMTP and PHP's "mail" function as drivers for the - | sending of e-mail. You may specify which one you're using throughout - | your application here. By default, Laravel is setup for SMTP mail. - | - | Supported: "smtp", "sendmail", "mailgun", "mandrill", "ses", - | "sparkpost", "postmark", "log", "array" + | This option controls the default mailer that is used to send any email + | messages sent by your application. Alternative mailers may be setup + | and used as needed; however, this mailer will be used by default. | */ - 'driver' => env('MAIL_DRIVER', 'smtp'), + 'default' => env('MAIL_MAILER', 'smtp'), /* |-------------------------------------------------------------------------- - | SMTP Host Address + | Mailer Configurations |-------------------------------------------------------------------------- | - | Here you may provide the host address of the SMTP server used by your - | applications. A default option is provided that is compatible with - | the Mailgun mail service which will provide reliable deliveries. + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. | - */ - - 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), - - /* - |-------------------------------------------------------------------------- - | SMTP Host Port - |-------------------------------------------------------------------------- + | Laravel supports a variety of mail "transport" drivers to be used while + | sending an e-mail. You will specify which one you are using for your + | mailers below. You are free to add additional mailers as required. | - | This is the SMTP port used by your application to deliver e-mails to - | users of the application. Like the host we have set this value to - | stay compatible with the Mailgun e-mail application by default. + | Supported: "smtp", "sendmail", "mailgun", "ses", + | "postmark", "log", "array", "failover" | */ - 'port' => env('MAIL_PORT', 587), + 'mailers' => [ + 'smtp' => [ + 'transport' => 'smtp', + 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), + 'port' => env('MAIL_PORT', 587), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'mailgun' => [ + 'transport' => 'mailgun', + ], + + 'postmark' => [ + 'transport' => 'postmark', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -t -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + ], + ], /* |-------------------------------------------------------------------------- @@ -78,47 +113,6 @@ /* |-------------------------------------------------------------------------- - | E-Mail Encryption Protocol - |-------------------------------------------------------------------------- - | - | Here you may specify the encryption protocol that should be used when - | the application send e-mail messages. A sensible default using the - | transport layer security protocol should provide great security. - | - */ - - 'encryption' => env('MAIL_ENCRYPTION', 'tls'), - - /* - |-------------------------------------------------------------------------- - | SMTP Server Username - |-------------------------------------------------------------------------- - | - | If your SMTP server requires a username for authentication, you should - | set it here. This will get used to authenticate with your server on - | connection. You may also set the "password" value below this one. - | - */ - - 'username' => env('MAIL_USERNAME'), - - 'password' => env('MAIL_PASSWORD'), - - /* - |-------------------------------------------------------------------------- - | Sendmail System Path - |-------------------------------------------------------------------------- - | - | When using the "sendmail" driver to send e-mails, we will need to know - | the path to where Sendmail lives on this server. A default path has - | been provided here, which will work well on most of your systems. - | - */ - - 'sendmail' => '/usr/sbin/sendmail -bs', - - /* - |-------------------------------------------------------------------------- | Markdown Mail Settings |-------------------------------------------------------------------------- | @@ -132,21 +126,8 @@ 'theme' => 'default', 'paths' => [ - resource_path('views/emails'), + resource_path('views/vendor/mail'), ], ], - /* - |-------------------------------------------------------------------------- - | Log Channel - |-------------------------------------------------------------------------- - | - | If you are using the "log" driver, you may specify the logging channel - | if you prefer to keep mail messages separate from other log entries - | for simpler reading. Otherwise, the default channel will be used. - | - */ - - 'log_channel' => env('MAIL_LOG_CHANNEL'), - ]; diff --git a/src/config/octane.php b/src/config/octane.php new file mode 100644 --- /dev/null +++ b/src/config/octane.php @@ -0,0 +1,246 @@ + env('OCTANE_SERVER', 'swoole'), + + /* + |-------------------------------------------------------------------------- + | Force HTTPS + |-------------------------------------------------------------------------- + | + | When this configuration value is set to "true", Octane will inform the + | framework that all absolute links must be generated using the HTTPS + | protocol. Otherwise your links may be generated using plain HTTP. + | + */ + + 'https' => env('OCTANE_HTTPS', true), + + /* + |-------------------------------------------------------------------------- + | Octane Listeners + |-------------------------------------------------------------------------- + | + | All of the event listeners for Octane's events are defined below. These + | listeners are responsible for resetting your application's state for + | the next request. You may even add your own listeners to the list. + | + */ + + 'listeners' => [ + WorkerStarting::class => [ + EnsureUploadedFilesAreValid::class, + EnsureUploadedFilesCanBeMoved::class, + ], + + RequestReceived::class => [ + ...Octane::prepareApplicationForNextOperation(), + ...Octane::prepareApplicationForNextRequest(), + // + ], + + RequestHandled::class => [ + // + ], + + RequestTerminated::class => [ + // FlushUploadedFiles::class, + ], + + TaskReceived::class => [ + ...Octane::prepareApplicationForNextOperation(), + // + ], + + TaskTerminated::class => [ + // + ], + + TickReceived::class => [ + ...Octane::prepareApplicationForNextOperation(), + // + ], + + TickTerminated::class => [ + // + ], + + OperationTerminated::class => [ + FlushTemporaryContainerInstances::class, + // DisconnectFromDatabases::class, + CollectGarbage::class, + ], + + WorkerErrorOccurred::class => [ + ReportException::class, + StopWorkerIfNecessary::class, + ], + + WorkerStopping::class => [ + // + ], + ], + + /* + |-------------------------------------------------------------------------- + | Warm / Flush Bindings + |-------------------------------------------------------------------------- + | + | The bindings listed below will either be pre-warmed when a worker boots + | or they will be flushed before every new request. Flushing a binding + | will force the container to resolve that binding again when asked. + | + */ + + 'warm' => [ + ...Octane::defaultServicesToWarm(), + ], + + 'flush' => [ + ], + + /* + |-------------------------------------------------------------------------- + | Octane Cache Table + |-------------------------------------------------------------------------- + | + | While using Swoole, you may leverage the Octane cache, which is powered + | by a Swoole table. You may set the maximum number of rows as well as + | the number of bytes per row using the configuration options below. + | + */ + + 'cache' => [ + 'rows' => 1000, + 'bytes' => 10000, + ], + + /* + |-------------------------------------------------------------------------- + | Octane Swoole Tables + |-------------------------------------------------------------------------- + | + | While using Swoole, you may define additional tables as required by the + | application. These tables can be used to store data that needs to be + | quickly accessed by other workers on the particular Swoole server. + | + */ + + 'tables' => [ +/* + 'example:1000' => [ + 'name' => 'string:1000', + 'votes' => 'int', + ], +*/ + ], + + /* + |-------------------------------------------------------------------------- + | File Watching + |-------------------------------------------------------------------------- + | + | The following list of files and directories will be watched when using + | the --watch option offered by Octane. If any of the directories and + | files are changed, Octane will automatically reload your workers. + | + */ + + 'watch' => [ + 'app', + 'bootstrap', + 'config', + 'database', + 'public/**/*.php', + 'resources/**/*.php', + 'routes', + 'composer.lock', + '.env', + ], + + /* + |-------------------------------------------------------------------------- + | Garbage Collection Threshold + |-------------------------------------------------------------------------- + | + | When executing long-lived PHP scripts such as Octane, memory can build + | up before being cleared by PHP. You can force Octane to run garbage + | collection if your application consumes this amount of megabytes. + | + */ + + 'garbage' => 64, + + /* + |-------------------------------------------------------------------------- + | Maximum Execution Time + |-------------------------------------------------------------------------- + | + | The following setting configures the maximum execution time for requests + | being handled by Octane. You may set this value to 0 to indicate that + | there isn't a specific time limit on Octane request execution time. + | + */ + + 'max_execution_time' => 30, + + /* + |-------------------------------------------------------------------------- + | Swoole configuration + |-------------------------------------------------------------------------- + | + | See Laravel\Octane\Command\StartSwooleCommand + */ + + 'swoole' => [ + 'options' => [ + 'log_file' => storage_path('logs/swoole_http.log'), + 'package_max_length' => 10 * 1024 * 1024, + 'enable_coroutine' => false, + //FIXME the daemonize option does not work + // 'daemonize' => env('OCTANE_DAEMONIZE', true), + //FIXME accessing app()->environment in here renders artisan disfunctional. I suppose it's too early. + //'log_level' => app()->environment('local') ? SWOOLE_LOG_INFO : SWOOLE_LOG_ERROR, + // 'reactor_num' => , // number of available cpus by default + 'send_yield' => true, + 'socket_buffer_size' => 10 * 1024 * 1024, + // 'task_worker_num' => // number of available cpus by default + // 'worker_num' => // number of available cpus by default + ], + ], +]; diff --git a/src/config/queue.php b/src/config/queue.php --- a/src/config/queue.php +++ b/src/config/queue.php @@ -39,6 +39,7 @@ 'table' => 'jobs', 'queue' => 'default', 'retry_after' => 90, + 'after_commit' => false, ], 'beanstalkd' => [ @@ -47,6 +48,7 @@ 'queue' => 'default', 'retry_after' => 90, 'block_for' => 0, + 'after_commit' => false, ], 'sqs' => [ @@ -54,8 +56,10 @@ 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), - 'queue' => env('SQS_QUEUE', 'your-queue-name'), + 'queue' => env('SQS_QUEUE', 'default'), + 'suffix' => env('SQS_SUFFIX'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, ], 'redis' => [ @@ -64,6 +68,7 @@ 'queue' => env('REDIS_QUEUE', 'default'), 'retry_after' => 90, 'block_for' => null, + 'after_commit' => false, ], ], diff --git a/src/config/session.php b/src/config/session.php --- a/src/config/session.php +++ b/src/config/session.php @@ -72,7 +72,7 @@ | */ - 'connection' => env('SESSION_CONNECTION', null), + 'connection' => env('SESSION_CONNECTION'), /* |-------------------------------------------------------------------------- @@ -92,13 +92,15 @@ | Session Cache Store |-------------------------------------------------------------------------- | - | When using the "apc", "memcached", or "dynamodb" session drivers you may + | While using one of the framework's cache driven session backends you may | list a cache store that should be used for these sessions. This value | must match with one of the application's configured cache "stores". | + | Affects: "apc", "dynamodb", "memcached", "redis" + | */ - 'store' => env('SESSION_STORE', null), + 'store' => env('SESSION_STORE'), /* |-------------------------------------------------------------------------- @@ -124,10 +126,7 @@ | */ - 'cookie' => env( - 'SESSION_COOKIE', - Str::slug(env('APP_NAME', 'laravel'), '_') . '_session' - ), + 'cookie' => env('SESSION_COOKIE', Str::slug(env('APP_NAME', 'laravel'), '_') . '_session'), /* |-------------------------------------------------------------------------- @@ -153,7 +152,7 @@ | */ - 'domain' => env('SESSION_DOMAIN', null), + 'domain' => env('SESSION_DOMAIN'), /* |-------------------------------------------------------------------------- @@ -162,11 +161,11 @@ | | By setting this option to true, session cookies will only be sent back | to the server if the browser has a HTTPS connection. This will keep - | the cookie from being sent to you if it can not be done securely. + | the cookie from being sent to you when it can't be done securely. | */ - 'secure' => env('SESSION_SECURE_COOKIE', false), + 'secure' => env('SESSION_SECURE_COOKIE'), /* |-------------------------------------------------------------------------- @@ -188,12 +187,12 @@ | | This option determines how your cookies behave when cross-site requests | take place, and can be used to mitigate CSRF attacks. By default, we - | do not enable this as other CSRF protection services are in place. + | will set this value to "lax" since this is a secure default value. | - | Supported: "lax", "strict" + | Supported: "lax", "strict", "none", null | */ - 'same_site' => null, + 'same_site' => 'lax', ]; diff --git a/src/config/swoole_http.php b/src/config/swoole_http.php deleted file mode 100644 --- a/src/config/swoole_http.php +++ /dev/null @@ -1,141 +0,0 @@ - [ - 'host' => env('SWOOLE_HTTP_HOST', '127.0.0.1'), - 'port' => env('SWOOLE_HTTP_PORT', '1215'), - 'public_path' => base_path('public'), - // Determine if to use swoole to respond request for static files - 'handle_static_files' => env('SWOOLE_HANDLE_STATIC', true), - 'access_log' => env('SWOOLE_HTTP_ACCESS_LOG', false), - // You must add --enable-openssl while compiling Swoole - // Put `SWOOLE_SOCK_TCP | SWOOLE_SSL` if you want to enable SSL - 'socket_type' => SWOOLE_SOCK_TCP, - 'process_type' => SWOOLE_PROCESS, - 'options' => [ - 'pid_file' => env('SWOOLE_HTTP_PID_FILE', base_path('storage/logs/swoole_http.pid')), - 'log_file' => env('SWOOLE_HTTP_LOG_FILE', base_path('storage/logs/swoole_http.log')), - 'daemonize' => env('SWOOLE_HTTP_DAEMONIZE', false), - // Normally this value should be 1~4 times larger according to your cpu cores. - 'reactor_num' => env('SWOOLE_HTTP_REACTOR_NUM', swoole_cpu_num()), - 'worker_num' => env('SWOOLE_HTTP_WORKER_NUM', swoole_cpu_num()), - 'task_worker_num' => env('SWOOLE_HTTP_TASK_WORKER_NUM', swoole_cpu_num()), - // The data to receive can't be larger than buffer_output_size. - 'package_max_length' => 20 * 1024 * 1024, - // The data to send can't be larger than buffer_output_size. - 'buffer_output_size' => 10 * 1024 * 1024, - // Max buffer size for socket connections - 'socket_buffer_size' => 128 * 1024 * 1024, - // Worker will restart after processing this number of requests - 'max_request' => 3000, - // Enable coroutine send - 'send_yield' => true, - // You must add --enable-openssl while compiling Swoole - 'ssl_cert_file' => null, - 'ssl_key_file' => null, - ], - ], - - /* - |-------------------------------------------------------------------------- - | Enable to turn on websocket server. - |-------------------------------------------------------------------------- - */ - 'websocket' => [ - 'enabled' => env('SWOOLE_HTTP_WEBSOCKET', false), - ], - - /* - |-------------------------------------------------------------------------- - | Hot reload configuration - |-------------------------------------------------------------------------- - */ - 'hot_reload' => [ - 'enabled' => env('SWOOLE_HOT_RELOAD_ENABLE', false), - 'recursively' => env('SWOOLE_HOT_RELOAD_RECURSIVELY', true), - 'directory' => env('SWOOLE_HOT_RELOAD_DIRECTORY', base_path()), - 'log' => env('SWOOLE_HOT_RELOAD_LOG', true), - 'filter' => env('SWOOLE_HOT_RELOAD_FILTER', '.php'), - ], - - /* - |-------------------------------------------------------------------------- - | Console output will be transferred to response content if enabled. - |-------------------------------------------------------------------------- - */ - 'ob_output' => env('SWOOLE_OB_OUTPUT', true), - - /* - |-------------------------------------------------------------------------- - | Pre-resolved instances here will be resolved when sandbox created. - |-------------------------------------------------------------------------- - */ - 'pre_resolved' => [ - 'view', 'files', 'session', 'session.store', 'routes', - 'db', 'db.factory', 'cache', 'cache.store', 'config', 'cookie', - 'encrypter', 'hash', 'router', 'translator', 'url', 'log', - ], - - /* - |-------------------------------------------------------------------------- - | Instances here will be cleared on every request. - |-------------------------------------------------------------------------- - */ - 'instances' => [ - 'auth', 'translator' - ], - - /* - |-------------------------------------------------------------------------- - | Providers here will be registered on every request. - |-------------------------------------------------------------------------- - */ - 'providers' => [ - Illuminate\Pagination\PaginationServiceProvider::class, - App\Providers\AuthServiceProvider::class, - //Without this passport will sort of work, - //but PassportServiceProvider will not contain a valid app instance. - App\Providers\PassportServiceProvider::class, - ], - - /* - |-------------------------------------------------------------------------- - | Resetters for sandbox app. - |-------------------------------------------------------------------------- - */ - 'resetters' => [ - SwooleTW\Http\Server\Resetters\ResetConfig::class, - SwooleTW\Http\Server\Resetters\ResetSession::class, - SwooleTW\Http\Server\Resetters\ResetCookie::class, - SwooleTW\Http\Server\Resetters\ClearInstances::class, - SwooleTW\Http\Server\Resetters\BindRequest::class, - SwooleTW\Http\Server\Resetters\RebindKernelContainer::class, - SwooleTW\Http\Server\Resetters\RebindRouterContainer::class, - SwooleTW\Http\Server\Resetters\RebindViewContainer::class, - SwooleTW\Http\Server\Resetters\ResetProviders::class, - ], - - /* - |-------------------------------------------------------------------------- - | Define your swoole tables here. - | - | @see https://www.swoole.co.uk/docs/modules/swoole-table - |-------------------------------------------------------------------------- - */ - 'tables' => [ - // 'table_name' => [ - // 'size' => 1024, - // 'columns' => [ - // ['name' => 'column_name', 'type' => Table::TYPE_STRING, 'size' => 1024], - // ] - // ], - ], -]; diff --git a/src/config/swoole_websocket.php b/src/config/swoole_websocket.php deleted file mode 100644 --- a/src/config/swoole_websocket.php +++ /dev/null @@ -1,107 +0,0 @@ - SwooleTW\Http\Websocket\SocketIO\WebsocketHandler::class, - - /* - |-------------------------------------------------------------------------- - | Default frame parser - | Replace it if you want to customize your websocket payload - |-------------------------------------------------------------------------- - */ - 'parser' => SwooleTW\Http\Websocket\SocketIO\SocketIOParser::class, - - /* - |-------------------------------------------------------------------------- - | Websocket route file path - |-------------------------------------------------------------------------- - */ - 'route_file' => base_path('routes/websocket.php'), - - /* - |-------------------------------------------------------------------------- - | Default middleware for on connect request - |-------------------------------------------------------------------------- - */ - 'middleware' => [ - SwooleTW\Http\Websocket\Middleware\DecryptCookies::class, - SwooleTW\Http\Websocket\Middleware\StartSession::class, - SwooleTW\Http\Websocket\Middleware\Authenticate::class, - ], - - /* - |-------------------------------------------------------------------------- - | Websocket handler for customized onHandShake callback - |-------------------------------------------------------------------------- - */ - 'handshake' => [ - 'enabled' => false, - 'handler' => SwooleTW\Http\Websocket\HandShakeHandler::class, - ], - - /* - |-------------------------------------------------------------------------- - | Default websocket driver - |-------------------------------------------------------------------------- - */ - 'default' => 'table', - - /* - |-------------------------------------------------------------------------- - | Websocket client's heartbeat interval (ms) - |-------------------------------------------------------------------------- - */ - 'ping_interval' => 25000, - - /* - |-------------------------------------------------------------------------- - | Websocket client's heartbeat interval timeout (ms) - |-------------------------------------------------------------------------- - */ - 'ping_timeout' => 60000, - - /* - |-------------------------------------------------------------------------- - | Room drivers mapping - |-------------------------------------------------------------------------- - */ - 'drivers' => [ - 'table' => SwooleTW\Http\Websocket\Rooms\TableRoom::class, - 'redis' => SwooleTW\Http\Websocket\Rooms\RedisRoom::class, - ], - - /* - |-------------------------------------------------------------------------- - | Room drivers settings - |-------------------------------------------------------------------------- - */ - 'settings' => [ - - 'table' => [ - 'room_rows' => 4096, - 'room_size' => 2048, - 'client_rows' => 8192, - 'client_size' => 2048, - ], - - 'redis' => [ - 'server' => [ - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'password' => env('REDIS_PASSWORD', null), - 'port' => env('REDIS_PORT', 6379), - 'database' => 0, - 'persistent' => true, - ], - 'options' => [ - // - ], - 'prefix' => 'swoole:', - ], - ], -]; diff --git a/src/include/Kolab2FA/Driver/HOTP.php b/src/include/Kolab2FA/Driver/HOTP.php --- a/src/include/Kolab2FA/Driver/HOTP.php +++ b/src/include/Kolab2FA/Driver/HOTP.php @@ -58,12 +58,15 @@ ); // copy config options - $this->backend = new \Kolab2FA\OTP\HOTP(); - $this->backend - ->setDigits($this->config['digits']) - ->setDigest($this->config['digest']) - ->setIssuer($this->config['issuer']) - ->setIssuerIncludedAsParameter(true); + $this->backend = \OTPHP\HOTP::create( + null, + 0, + $this->config['digest'], + $this->config['digits'] + ); + + $this->backend->setIssuer($this->config['issuer']); + $this->backend->setIssuerIncludedAsParameter(true); } /** @@ -73,7 +76,7 @@ { // get my secret from the user storage $secret = $this->get('secret'); - $counter = $this->get('counter'); + $counter = (int) $this->get('counter'); if (!strlen($secret)) { // LOG: "no secret set for user $this->username" @@ -82,7 +85,10 @@ } try { - $this->backend->setLabel($this->username)->setSecret($secret)->setCounter(intval($this->get('counter'))); + $this->backend->setLabel($this->username); + $this->backend->setSecret($secret); + $this->backend->setParameter('counter', $counter); + $pass = $this->backend->verify($code, $counter, $this->config['window']); // store incremented counter value @@ -114,7 +120,10 @@ // TODO: deny call if already active? - $this->backend->setLabel($this->username)->setSecret($this->secret)->setCounter(intval($this->get('counter'))); + $this->backend->setLabel($this->username); + $this->backend->setSecret($this->secret); + $this->backend->setParameter('counter', (int) $this->get('counter')); + return $this->backend->getProvisioningUri(); } diff --git a/src/include/Kolab2FA/Driver/TOTP.php b/src/include/Kolab2FA/Driver/TOTP.php --- a/src/include/Kolab2FA/Driver/TOTP.php +++ b/src/include/Kolab2FA/Driver/TOTP.php @@ -52,13 +52,15 @@ ); // copy config options - $this->backend = new \Kolab2FA\OTP\TOTP(); - $this->backend - ->setDigits($this->config['digits']) - ->setInterval($this->config['interval']) - ->setDigest($this->config['digest']) - ->setIssuer($this->config['issuer']) - ->setIssuerIncludedAsParameter(true); + $this->backend = \OTPHP\TOTP::create( + null, + $this->config['interval'], + $this->config['digest'], + $this->config['digits'] + ); + + $this->backend->setIssuer($this->config['issuer']); + $this->backend->setIssuerIncludedAsParameter(true); } /** @@ -75,14 +77,11 @@ return false; } - $this->backend->setLabel($this->username)->setSecret($secret); + $this->backend->setLabel($this->username); + $this->backend->setParameter('secret', $secret); - // PHP gets a string, but we're comparing integers. - $code = (int)$code; -//$code = (string) $code; - // Pass a window to indicate the maximum timeslip between client (mobile - // device) and server. - $pass = $this->backend->verify($code, $timestamp, 150); + // Pass a window to indicate the maximum timeslip between client (device) and server. + $pass = $this->backend->verify((string) $code, $timestamp, 150); // try all codes from $timestamp till now if (!$pass && $timestamp) { @@ -109,7 +108,8 @@ return; } - $this->backend->setLabel($this->username)->setSecret($secret); + $this->backend->setLabel($this->username); + $this->backend->setParameter('secret', $secret); return $this->backend->at(time()); } @@ -130,8 +130,9 @@ // TODO: deny call if already active? - $this->backend->setLabel($this->username)->setSecret($this->secret); + $this->backend->setLabel($this->username); + $this->backend->setParameter('secret', $secret); + return $this->backend->getProvisioningUri(); } - } diff --git a/src/include/Kolab2FA/OTP/HOTP.php b/src/include/Kolab2FA/OTP/HOTP.php deleted file mode 100644 --- a/src/include/Kolab2FA/OTP/HOTP.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * Copyright (C) 2015, Kolab Systems AG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - - -namespace Kolab2FA\OTP; - -use OTPHP\HOTP as Base; - -class HOTP extends Base -{ - use OTP; - protected $counter = 0; - - public function setCounter($counter) - { - if (!is_integer($counter) || $counter < 0) { - throw new \Exception('Counter must be at least 0.'); - } - $this->counter = $counter; - - return $this; - } - - public function getCounter() - { - return $this->counter; - } - - public function updateCounter($counter) - { - $this->counter = $counter; - - return $this; - } -} diff --git a/src/include/Kolab2FA/OTP/OTP.php b/src/include/Kolab2FA/OTP/OTP.php deleted file mode 100644 --- a/src/include/Kolab2FA/OTP/OTP.php +++ /dev/null @@ -1,133 +0,0 @@ - - * - * Copyright (C) 2015, Kolab Systems AG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -namespace Kolab2FA\OTP; - -trait OTP -{ - protected $secret = null; - protected $issuer = null; - protected $issuer_included_as_parameter = false; - protected $label = null; - protected $digest = 'sha1'; - protected $digits = 6; - - public function setSecret($secret) - { - $this->secret = $secret; - - return $this; - } - - public function getSecret() - { - return $this->secret; - } - - public function setLabel($label) - { - if ($this->hasSemicolon($label)) { - throw new \Exception('Label must not contain a semi-colon.'); - } - $this->label = $label; - - return $this; - } - - public function getLabel() - { - return $this->label; - } - - public function setIssuer($issuer) - { - if ($this->hasSemicolon($issuer)) { - throw new \Exception('Issuer must not contain a semi-colon.'); - } - $this->issuer = $issuer; - - return $this; - } - - public function getIssuer() - { - return $this->issuer; - } - - public function isIssuerIncludedAsParameter() - { - return $this->issuer_included_as_parameter; - } - - public function setIssuerIncludedAsParameter($issuer_included_as_parameter) - { - $this->issuer_included_as_parameter = $issuer_included_as_parameter; - - return $this; - } - - public function setDigits($digits) - { - if (!is_numeric($digits) || $digits < 1) { - throw new \Exception('Digits must be at least 1.'); - } - $this->digits = $digits; - - return $this; - } - - public function getDigits() - { - return $this->digits; - } - - public function setDigest($digest) - { - if (!in_array($digest, array('md5', 'sha1', 'sha256', 'sha512'))) { - throw new \Exception("'$digest' digest is not supported."); - } - $this->digest = $digest; - - return $this; - } - - public function getDigest() - { - return $this->digest; - } - - private function hasSemicolon($value) - { - $semicolons = array(':', '%3A', '%3a'); - foreach ($semicolons as $semicolon) { - if (false !== strpos($value, $semicolon)) { - return true; - } - } - - return false; - } -} diff --git a/src/include/Kolab2FA/OTP/TOTP.php b/src/include/Kolab2FA/OTP/TOTP.php deleted file mode 100644 --- a/src/include/Kolab2FA/OTP/TOTP.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * Copyright (C) 2015, Kolab Systems AG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -namespace Kolab2FA\OTP; - -use OTPHP\TOTP as Base; - -class TOTP extends Base -{ - use OTP; - protected $interval = 30; - - public function setInterval($interval) - { - if (!is_integer($interval) || $interval < 1) { - throw new \Exception('Interval must be at least 1.'); - } - $this->interval = $interval; - - return $this; - } - - public function getInterval() - { - return $this->interval; - } -} diff --git a/src/phpstan.neon b/src/phpstan.neon --- a/src/phpstan.neon +++ b/src/phpstan.neon @@ -8,7 +8,6 @@ - '#Call to an undefined [a-zA-Z0-9<>\\ ]+::withObjectTenantContext\(\)#' - '#Call to an undefined [a-zA-Z0-9<>\\ ]+::withSubjectTenantContext\(\)#' - '#Call to an undefined method Tests\\Browser::#' - - '#Call to an undefined method Illuminate\\Support\\Fluent::references\(\)#' level: 4 parallel: processTimeout: 300.0 @@ -17,4 +16,5 @@ - config/ - database/ - resources/ + - routes/ - tests/ diff --git a/src/phpunit.xml b/src/phpunit.xml --- a/src/phpunit.xml +++ b/src/phpunit.xml @@ -37,7 +37,7 @@ - + diff --git a/src/public/index.php b/src/public/index.php --- a/src/public/index.php +++ b/src/public/index.php @@ -1,14 +1,14 @@ - */ +use Illuminate\Contracts\Http\Kernel; +use Illuminate\Http\Request; define('LARAVEL_START', microtime(true)); +if (file_exists($maintenance = __DIR__ . '/../storage/framework/maintenance.php')) { + require $maintenance; +} + /* |-------------------------------------------------------------------------- | Register The Auto Loader @@ -21,7 +21,7 @@ | */ -require __DIR__.'/../vendor/autoload.php'; +require __DIR__ . '/../vendor/autoload.php'; /* |-------------------------------------------------------------------------- @@ -35,7 +35,7 @@ | */ -$app = require_once __DIR__.'/../bootstrap/app.php'; +$app = require_once __DIR__ . '/../bootstrap/app.php'; /* |-------------------------------------------------------------------------- @@ -49,10 +49,10 @@ | */ -$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); +$kernel = $app->make(Kernel::class); $response = $kernel->handle( - $request = Illuminate\Http\Request::capture() + $request = Request::capture() ); $response->send(); diff --git a/src/resources/lang/en/auth.php b/src/resources/lang/en/auth.php --- a/src/resources/lang/en/auth.php +++ b/src/resources/lang/en/auth.php @@ -14,6 +14,7 @@ */ 'failed' => 'Invalid username or password.', + 'password' => 'The provided password is incorrect.', 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 'logoutsuccess' => 'Successfully logged out.', diff --git a/src/resources/lang/en/validation.php b/src/resources/lang/en/validation.php --- a/src/resources/lang/en/validation.php +++ b/src/resources/lang/en/validation.php @@ -14,12 +14,13 @@ */ 'accepted' => 'The :attribute must be accepted.', + 'accepted_if' => 'The :attribute must be accepted when :other is :value.', 'active_url' => 'The :attribute is not a valid URL.', 'after' => 'The :attribute must be a date after :date.', 'after_or_equal' => 'The :attribute must be a date after or equal to :date.', - 'alpha' => 'The :attribute may only contain letters.', - 'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.', - 'alpha_num' => 'The :attribute may only contain letters and numbers.', + 'alpha' => 'The :attribute must only contain letters.', + 'alpha_dash' => 'The :attribute must only contain letters, numbers, dashes and underscores.', + 'alpha_num' => 'The :attribute must only contain letters and numbers.', 'array' => 'The :attribute must be an array.', 'before' => 'The :attribute must be a date before :date.', 'before_or_equal' => 'The :attribute must be a date before or equal to :date.', @@ -31,9 +32,12 @@ ], 'boolean' => 'The :attribute field must be true or false.', 'confirmed' => 'The :attribute confirmation does not match.', + 'current_password' => 'The password is incorrect.', 'date' => 'The :attribute is not a valid date.', 'date_equals' => 'The :attribute must be a date equal to :date.', 'date_format' => 'The :attribute does not match the format :format.', + 'declined' => 'The :attribute must be declined.', + 'declined_if' => 'The :attribute must be declined when :other is :value.', 'different' => 'The :attribute and :other must be different.', 'digits' => 'The :attribute must be :digits digits.', 'digits_between' => 'The :attribute must be between :min and :max digits.', @@ -41,6 +45,7 @@ 'distinct' => 'The :attribute field has a duplicate value.', 'email' => 'The :attribute must be a valid email address.', 'ends_with' => 'The :attribute must end with one of the following: :values', + 'enum' => 'The selected :attribute is invalid.', 'exists' => 'The selected :attribute is invalid.', 'file' => 'The :attribute must be a file.', 'filled' => 'The :attribute field must have a value.', @@ -82,6 +87,7 @@ 'string' => 'The :attribute may not be greater than :max characters.', 'array' => 'The :attribute may not have more than :max items.', ], + 'mac_address' => 'The :attribute must be a valid MAC address.', 'mimes' => 'The :attribute must be a file of type: :values.', 'mimetypes' => 'The :attribute must be a file of type: :values.', 'min' => [ @@ -90,12 +96,18 @@ 'string' => 'The :attribute must be at least :min characters.', 'array' => 'The :attribute must have at least :min items.', ], + 'multiple_of' => 'The :attribute must be a multiple of :value.', 'not_in' => 'The selected :attribute is invalid.', 'not_regex' => 'The :attribute format is invalid.', 'numeric' => 'The :attribute must be a number.', 'present' => 'The :attribute field must be present.', + 'prohibited' => 'The :attribute field is prohibited.', + 'prohibited_if' => 'The :attribute field is prohibited when :other is :value.', + 'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.', + 'prohibits' => 'The :attribute field prohibits :other from being present.', 'regex' => 'The :attribute format is invalid.', 'required' => 'The :attribute field is required.', + 'required_array_keys' => 'The :attribute field must contain entries for: :values.', 'required_if' => 'The :attribute field is required when :other is :value.', 'required_unless' => 'The :attribute field is required unless :other is in :values.', 'required_with' => 'The :attribute field is required when :values is present.', @@ -111,10 +123,10 @@ ], 'starts_with' => 'The :attribute must start with one of the following: :values', 'string' => 'The :attribute must be a string.', - 'timezone' => 'The :attribute must be a valid zone.', + 'timezone' => 'The :attribute must be a valid timezone.', 'unique' => 'The :attribute has already been taken.', 'uploaded' => 'The :attribute failed to upload.', - 'url' => 'The :attribute format is invalid.', + 'url' => 'The :attribute must be a valid URL.', 'uuid' => 'The :attribute must be a valid UUID.', '2fareq' => 'Second factor code is required.', diff --git a/src/routes/api.php b/src/routes/api.php --- a/src/routes/api.php +++ b/src/routes/api.php @@ -1,6 +1,8 @@ 'api', - 'prefix' => $prefix . 'api/auth' + 'prefix' => 'auth' ], - function ($router) { - Route::post('login', 'API\AuthController@login'); + function () { + Route::post('login', [API\AuthController::class, 'login']); Route::group( ['middleware' => 'auth:api'], - function ($router) { - Route::get('info', 'API\AuthController@info'); - Route::post('info', 'API\AuthController@info'); - Route::post('logout', 'API\AuthController@logout'); - Route::post('refresh', 'API\AuthController@refresh'); + function () { + Route::get('info', [API\AuthController::class, 'info']); + Route::post('info', [API\AuthController::class, 'info']); + Route::post('logout', [API\AuthController::class, 'logout']); + Route::post('refresh', [API\AuthController::class, 'refresh']); } ); } @@ -39,131 +39,115 @@ [ 'domain' => \config('app.website_domain'), 'middleware' => 'api', - 'prefix' => $prefix . 'api/auth' - ], - function ($router) { - Route::post('password-policy/check', 'API\PasswordPolicyController@check'); - - Route::post('password-reset/init', 'API\PasswordResetController@init'); - Route::post('password-reset/verify', 'API\PasswordResetController@verify'); - Route::post('password-reset', 'API\PasswordResetController@reset'); - - Route::post('signup/init', 'API\SignupController@init'); - Route::get('signup/invitations/{id}', 'API\SignupController@invitation'); - Route::get('signup/plans', 'API\SignupController@plans'); - Route::post('signup/verify', 'API\SignupController@verify'); - Route::post('signup', 'API\SignupController@signup'); - } -); - -Route::group( - [ - 'domain' => \config('app.website_domain'), - 'middleware' => 'auth:api', - 'prefix' => $prefix . 'api/v4' + 'prefix' => 'auth' ], function () { - Route::post('companion/register', 'API\V4\CompanionAppsController@register'); - - Route::post('auth-attempts/{id}/confirm', 'API\V4\AuthAttemptsController@confirm'); - Route::post('auth-attempts/{id}/deny', 'API\V4\AuthAttemptsController@deny'); - Route::get('auth-attempts/{id}/details', 'API\V4\AuthAttemptsController@details'); - Route::get('auth-attempts', 'API\V4\AuthAttemptsController@index'); - - Route::apiResource('domains', 'API\V4\DomainsController'); - Route::get('domains/{id}/confirm', 'API\V4\DomainsController@confirm'); - Route::get('domains/{id}/skus', 'API\V4\SkusController@domainSkus'); - Route::get('domains/{id}/status', 'API\V4\DomainsController@status'); - Route::post('domains/{id}/config', 'API\V4\DomainsController@setConfig'); - - Route::apiResource('groups', 'API\V4\GroupsController'); - Route::get('groups/{id}/status', 'API\V4\GroupsController@status'); - Route::post('groups/{id}/config', 'API\V4\GroupsController@setConfig'); - - Route::apiResource('packages', 'API\V4\PackagesController'); - - Route::apiResource('resources', 'API\V4\ResourcesController'); - Route::get('resources/{id}/status', 'API\V4\ResourcesController@status'); - Route::post('resources/{id}/config', 'API\V4\ResourcesController@setConfig'); - - Route::apiResource('shared-folders', 'API\V4\SharedFoldersController'); - Route::get('shared-folders/{id}/status', 'API\V4\SharedFoldersController@status'); - Route::post('shared-folders/{id}/config', 'API\V4\SharedFoldersController@setConfig'); - - Route::apiResource('skus', 'API\V4\SkusController'); - - Route::apiResource('users', 'API\V4\UsersController'); - Route::post('users/{id}/config', 'API\V4\UsersController@setConfig'); - Route::get('users/{id}/skus', 'API\V4\SkusController@userSkus'); - Route::get('users/{id}/status', 'API\V4\UsersController@status'); - - Route::apiResource('wallets', 'API\V4\WalletsController'); - Route::get('wallets/{id}/transactions', 'API\V4\WalletsController@transactions'); - Route::get('wallets/{id}/receipts', 'API\V4\WalletsController@receipts'); - Route::get('wallets/{id}/receipts/{receipt}', 'API\V4\WalletsController@receiptDownload'); - - Route::get('password-policy', 'API\PasswordPolicyController@index'); - Route::post('password-reset/code', 'API\PasswordResetController@codeCreate'); - Route::delete('password-reset/code/{id}', 'API\PasswordResetController@codeDelete'); - - Route::post('payments', 'API\V4\PaymentsController@store'); - //Route::delete('payments', 'API\V4\PaymentsController@cancel'); - Route::get('payments/mandate', 'API\V4\PaymentsController@mandate'); - Route::post('payments/mandate', 'API\V4\PaymentsController@mandateCreate'); - Route::put('payments/mandate', 'API\V4\PaymentsController@mandateUpdate'); - Route::delete('payments/mandate', 'API\V4\PaymentsController@mandateDelete'); - Route::get('payments/methods', 'API\V4\PaymentsController@paymentMethods'); - Route::get('payments/pending', 'API\V4\PaymentsController@payments'); - Route::get('payments/has-pending', 'API\V4\PaymentsController@hasPayments'); - - Route::get('openvidu/rooms', 'API\V4\OpenViduController@index'); - Route::post('openvidu/rooms/{id}/close', 'API\V4\OpenViduController@closeRoom'); - Route::post('openvidu/rooms/{id}/config', 'API\V4\OpenViduController@setRoomConfig'); + Route::post('password-policy/check', [API\PasswordPolicyController::class, 'check']); - // FIXME: I'm not sure about this one, should we use DELETE request maybe? - Route::post('openvidu/rooms/{id}/connections/{conn}/dismiss', 'API\V4\OpenViduController@dismissConnection'); - Route::put('openvidu/rooms/{id}/connections/{conn}', 'API\V4\OpenViduController@updateConnection'); - Route::post('openvidu/rooms/{id}/request/{reqid}/accept', 'API\V4\OpenViduController@acceptJoinRequest'); - Route::post('openvidu/rooms/{id}/request/{reqid}/deny', 'API\V4\OpenViduController@denyJoinRequest'); + Route::post('password-reset/init', [API\PasswordResetController::class, 'init']); + Route::post('password-reset/verify', [API\PasswordResetController::class, 'verify']); + Route::post('password-reset', [API\PasswordResetController::class, 'reset']); + + Route::post('signup/init', [API\SignupController::class, 'init']); + Route::get('signup/invitations/{id}', [API\SignupController::class, 'invitation']); + Route::get('signup/plans', [API\SignupController::class, 'plans']); + Route::post('signup/verify', [API\SignupController::class, 'verify']); + Route::post('signup', [API\SignupController::class, 'signup']); } ); -// Note: In Laravel 7.x we could just use withoutMiddleware() instead of a separate group Route::group( [ 'domain' => \config('app.website_domain'), - 'prefix' => $prefix . 'api/v4' + 'middleware' => 'auth:api', + 'prefix' => 'v4' ], function () { - Route::post('openvidu/rooms/{id}', 'API\V4\OpenViduController@joinRoom'); - Route::post('openvidu/rooms/{id}/connections', 'API\V4\OpenViduController@createConnection'); + Route::post('companion/register', [API\V4\CompanionAppsController::class, 'register']); + + Route::post('auth-attempts/{id}/confirm', [API\V4\AuthAttemptsController::class, 'confirm']); + Route::post('auth-attempts/{id}/deny', [API\V4\AuthAttemptsController::class, 'deny']); + Route::get('auth-attempts/{id}/details', [API\V4\AuthAttemptsController::class, 'details']); + Route::get('auth-attempts', [API\V4\AuthAttemptsController::class, 'index']); + + Route::apiResource('domains', API\V4\DomainsController::class); + Route::get('domains/{id}/confirm', [API\V4\DomainsController::class, 'confirm']); + Route::get('domains/{id}/skus', [API\V4\SkusController::class, 'domainSkus']); + Route::get('domains/{id}/status', [API\V4\DomainsController::class, 'status']); + Route::post('domains/{id}/config', [API\V4\DomainsController::class, 'setConfig']); + + Route::apiResource('groups', API\V4\GroupsController::class); + Route::get('groups/{id}/status', [API\V4\GroupsController::class, 'status']); + Route::post('groups/{id}/config', [API\V4\GroupsController::class, 'setConfig']); + + Route::apiResource('packages', API\V4\PackagesController::class); + + Route::apiResource('resources', API\V4\ResourcesController::class); + Route::get('resources/{id}/status', [API\V4\ResourcesController::class, 'status']); + Route::post('resources/{id}/config', [API\V4\ResourcesController::class, 'setConfig']); + + Route::apiResource('shared-folders', API\V4\SharedFoldersController::class); + Route::get('shared-folders/{id}/status', [API\V4\SharedFoldersController::class, 'status']); + Route::post('shared-folders/{id}/config', [API\V4\SharedFoldersController::class, 'setConfig']); + + Route::apiResource('skus', API\V4\SkusController::class); + + Route::apiResource('users', API\V4\UsersController::class); + Route::post('users/{id}/config', [API\V4\UsersController::class, 'setConfig']); + Route::get('users/{id}/skus', [API\V4\SkusController::class, 'userSkus']); + Route::get('users/{id}/status', [API\V4\UsersController::class, 'status']); + + Route::apiResource('wallets', API\V4\WalletsController::class); + Route::get('wallets/{id}/transactions', [API\V4\WalletsController::class, 'transactions']); + Route::get('wallets/{id}/receipts', [API\V4\WalletsController::class, 'receipts']); + Route::get('wallets/{id}/receipts/{receipt}', [API\V4\WalletsController::class, 'receiptDownload']); + + Route::get('password-policy', [API\PasswordPolicyController::class, 'index']); + Route::post('password-reset/code', [API\PasswordResetController::class, 'codeCreate']); + Route::delete('password-reset/code/{id}', [API\PasswordResetController::class, 'codeDelete']); + + Route::post('payments', [API\V4\PaymentsController::class, 'store']); + //Route::delete('payments', [API\V4\PaymentsController::class, 'cancel']); + Route::get('payments/mandate', [API\V4\PaymentsController::class, 'mandate']); + Route::post('payments/mandate', [API\V4\PaymentsController::class, 'mandateCreate']); + Route::put('payments/mandate', [API\V4\PaymentsController::class, 'mandateUpdate']); + Route::delete('payments/mandate', [API\V4\PaymentsController::class, 'mandateDelete']); + Route::get('payments/methods', [API\V4\PaymentsController::class, 'paymentMethods']); + Route::get('payments/pending', [API\V4\PaymentsController::class, 'payments']); + Route::get('payments/has-pending', [API\V4\PaymentsController::class, 'hasPayments']); + + Route::get('openvidu/rooms', [API\V4\OpenViduController::class, 'index']); + Route::post('openvidu/rooms/{id}/close', [API\V4\OpenViduController::class, 'closeRoom']); + Route::post('openvidu/rooms/{id}/config', [API\V4\OpenViduController::class, 'setRoomConfig']); + + Route::post('openvidu/rooms/{id}', [API\V4\OpenViduController::class, 'joinRoom']) + ->withoutMiddleware(['auth:api']); + Route::post('openvidu/rooms/{id}/connections', [API\V4\OpenViduController::class, 'createConnection']) + ->withoutMiddleware(['auth:api']); // FIXME: I'm not sure about this one, should we use DELETE request maybe? - Route::post('openvidu/rooms/{id}/connections/{conn}/dismiss', 'API\V4\OpenViduController@dismissConnection'); - Route::put('openvidu/rooms/{id}/connections/{conn}', 'API\V4\OpenViduController@updateConnection'); - Route::post('openvidu/rooms/{id}/request/{reqid}/accept', 'API\V4\OpenViduController@acceptJoinRequest'); - Route::post('openvidu/rooms/{id}/request/{reqid}/deny', 'API\V4\OpenViduController@denyJoinRequest'); - } -); - -Route::group( - [ - 'domain' => \config('app.website_domain'), - 'middleware' => 'api', - 'prefix' => $prefix . 'api/v4' - ], - function ($router) { - Route::post('support/request', 'API\V4\SupportController@request'); + Route::post('openvidu/rooms/{id}/connections/{conn}/dismiss', [API\V4\OpenViduController::class, 'dismissConnection']) + ->withoutMiddleware(['auth:api']); + Route::put('openvidu/rooms/{id}/connections/{conn}', [API\V4\OpenViduController::class, 'updateConnection']) + ->withoutMiddleware(['auth:api']); + Route::post('openvidu/rooms/{id}/request/{reqid}/accept', [API\V4\OpenViduController::class, 'acceptJoinRequest']) + ->withoutMiddleware(['auth:api']); + Route::post('openvidu/rooms/{id}/request/{reqid}/deny', [API\V4\OpenViduController::class, 'denyJoinRequest']) + ->withoutMiddleware(['auth:api']); + + Route::post('support/request', [API\V4\SupportController::class, 'request']) + ->withoutMiddleware(['auth:api']) + ->middleware(['api']); } ); Route::group( [ 'domain' => \config('app.website_domain'), - 'prefix' => $prefix . 'api/webhooks' + 'prefix' => 'webhooks' ], function () { - Route::post('payment/{provider}', 'API\V4\PaymentsController@webhook'); - Route::post('meet/openvidu', 'API\V4\OpenViduController@webhook'); + Route::post('payment/{provider}', [API\V4\PaymentsController::class, 'webhook']); + Route::post('meet/openvidu', [API\V4\OpenViduController::class, 'webhook']); } ); @@ -171,14 +155,14 @@ Route::group( [ 'domain' => 'services.' . \config('app.website_domain'), - 'prefix' => $prefix . 'api/webhooks' + 'prefix' => 'webhooks' ], function () { - Route::get('nginx', 'API\V4\NGINXController@authenticate'); - Route::get('nginx-httpauth', 'API\V4\NGINXController@httpauth'); - Route::post('policy/greylist', 'API\V4\PolicyController@greylist'); - Route::post('policy/ratelimit', 'API\V4\PolicyController@ratelimit'); - Route::post('policy/spf', 'API\V4\PolicyController@senderPolicyFramework'); + Route::get('nginx', [API\V4\NGINXController::class, 'authenticate']); + Route::get('nginx-httpauth', [API\V4\NGINXController::class, 'httpauth']); + Route::post('policy/greylist', [API\V4\PolicyController::class, 'greylist']); + Route::post('policy/ratelimit', [API\V4\PolicyController::class, 'ratelimit']); + Route::post('policy/spf', [API\V4\PolicyController::class, 'senderPolicyFramework']); } ); } @@ -188,33 +172,35 @@ [ 'domain' => 'admin.' . \config('app.website_domain'), 'middleware' => ['auth:api', 'admin'], - 'prefix' => $prefix . 'api/v4', + 'prefix' => 'v4', ], function () { - Route::apiResource('domains', 'API\V4\Admin\DomainsController'); - Route::get('domains/{id}/skus', 'API\V4\Admin\SkusController@domainSkus'); - Route::post('domains/{id}/suspend', 'API\V4\Admin\DomainsController@suspend'); - Route::post('domains/{id}/unsuspend', 'API\V4\Admin\DomainsController@unsuspend'); - - Route::apiResource('groups', 'API\V4\Admin\GroupsController'); - Route::post('groups/{id}/suspend', 'API\V4\Admin\GroupsController@suspend'); - Route::post('groups/{id}/unsuspend', 'API\V4\Admin\GroupsController@unsuspend'); - - Route::apiResource('resources', 'API\V4\Admin\ResourcesController'); - Route::apiResource('shared-folders', 'API\V4\Admin\SharedFoldersController'); - Route::apiResource('skus', 'API\V4\Admin\SkusController'); - Route::apiResource('users', 'API\V4\Admin\UsersController'); - Route::get('users/{id}/discounts', 'API\V4\Reseller\DiscountsController@userDiscounts'); - Route::post('users/{id}/reset2FA', 'API\V4\Admin\UsersController@reset2FA'); - Route::get('users/{id}/skus', 'API\V4\Admin\SkusController@userSkus'); - Route::post('users/{id}/skus/{sku}', 'API\V4\Admin\UsersController@setSku'); - Route::post('users/{id}/suspend', 'API\V4\Admin\UsersController@suspend'); - Route::post('users/{id}/unsuspend', 'API\V4\Admin\UsersController@unsuspend'); - Route::apiResource('wallets', 'API\V4\Admin\WalletsController'); - Route::post('wallets/{id}/one-off', 'API\V4\Admin\WalletsController@oneOff'); - Route::get('wallets/{id}/transactions', 'API\V4\Admin\WalletsController@transactions'); - - Route::get('stats/chart/{chart}', 'API\V4\Admin\StatsController@chart'); + Route::apiResource('domains', API\V4\Admin\DomainsController::class); + Route::get('domains/{id}/skus', [API\V4\Admin\SkusController::class, 'domainSkus']); + Route::post('domains/{id}/suspend', [API\V4\Admin\DomainsController::class, 'suspend']); + Route::post('domains/{id}/unsuspend', [API\V4\Admin\DomainsController::class, 'unsuspend']); + + Route::apiResource('groups', API\V4\Admin\GroupsController::class); + Route::post('groups/{id}/suspend', [API\V4\Admin\GroupsController::class, 'suspend']); + Route::post('groups/{id}/unsuspend', [API\V4\Admin\GroupsController::class, 'unsuspend']); + + Route::apiResource('resources', API\V4\Admin\ResourcesController::class); + Route::apiResource('shared-folders', API\V4\Admin\SharedFoldersController::class); + Route::apiResource('skus', API\V4\Admin\SkusController::class); + + Route::apiResource('users', API\V4\Admin\UsersController::class); + Route::get('users/{id}/discounts', [API\V4\Reseller\DiscountsController::class, 'userDiscounts']); + Route::post('users/{id}/reset2FA', [API\V4\Admin\UsersController::class, 'reset2FA']); + Route::get('users/{id}/skus', [API\V4\Admin\SkusController::class, 'userSkus']); + Route::post('users/{id}/skus/{sku}', [API\V4\Admin\UsersController::class, 'setSku']); + Route::post('users/{id}/suspend', [API\V4\Admin\UsersController::class, 'suspend']); + Route::post('users/{id}/unsuspend', [API\V4\Admin\UsersController::class, 'unsuspend']); + + Route::apiResource('wallets', API\V4\Admin\WalletsController::class); + Route::post('wallets/{id}/one-off', [API\V4\Admin\WalletsController::class, 'oneOff']); + Route::get('wallets/{id}/transactions', [API\V4\Admin\WalletsController::class, 'transactions']); + + Route::get('stats/chart/{chart}', [API\V4\Admin\StatsController::class, 'chart']); } ); } @@ -224,47 +210,49 @@ [ 'domain' => 'reseller.' . \config('app.website_domain'), 'middleware' => ['auth:api', 'reseller'], - 'prefix' => $prefix . 'api/v4', + 'prefix' => 'v4', ], function () { - Route::apiResource('domains', 'API\V4\Reseller\DomainsController'); - Route::get('domains/{id}/skus', 'API\V4\Reseller\SkusController@domainSkus'); - Route::post('domains/{id}/suspend', 'API\V4\Reseller\DomainsController@suspend'); - Route::post('domains/{id}/unsuspend', 'API\V4\Reseller\DomainsController@unsuspend'); - - Route::apiResource('groups', 'API\V4\Reseller\GroupsController'); - Route::post('groups/{id}/suspend', 'API\V4\Reseller\GroupsController@suspend'); - Route::post('groups/{id}/unsuspend', 'API\V4\Reseller\GroupsController@unsuspend'); - - Route::apiResource('invitations', 'API\V4\Reseller\InvitationsController'); - Route::post('invitations/{id}/resend', 'API\V4\Reseller\InvitationsController@resend'); - - Route::post('payments', 'API\V4\Reseller\PaymentsController@store'); - Route::get('payments/mandate', 'API\V4\Reseller\PaymentsController@mandate'); - Route::post('payments/mandate', 'API\V4\Reseller\PaymentsController@mandateCreate'); - Route::put('payments/mandate', 'API\V4\Reseller\PaymentsController@mandateUpdate'); - Route::delete('payments/mandate', 'API\V4\Reseller\PaymentsController@mandateDelete'); - Route::get('payments/methods', 'API\V4\Reseller\PaymentsController@paymentMethods'); - Route::get('payments/pending', 'API\V4\Reseller\PaymentsController@payments'); - Route::get('payments/has-pending', 'API\V4\Reseller\PaymentsController@hasPayments'); - - Route::apiResource('resources', 'API\V4\Reseller\ResourcesController'); - Route::apiResource('shared-folders', 'API\V4\Reseller\SharedFoldersController'); - Route::apiResource('skus', 'API\V4\Reseller\SkusController'); - Route::apiResource('users', 'API\V4\Reseller\UsersController'); - Route::get('users/{id}/discounts', 'API\V4\Reseller\DiscountsController@userDiscounts'); - Route::post('users/{id}/reset2FA', 'API\V4\Reseller\UsersController@reset2FA'); - Route::get('users/{id}/skus', 'API\V4\Reseller\SkusController@userSkus'); - Route::post('users/{id}/skus/{sku}', 'API\V4\Admin\UsersController@setSku'); - Route::post('users/{id}/suspend', 'API\V4\Reseller\UsersController@suspend'); - Route::post('users/{id}/unsuspend', 'API\V4\Reseller\UsersController@unsuspend'); - Route::apiResource('wallets', 'API\V4\Reseller\WalletsController'); - Route::post('wallets/{id}/one-off', 'API\V4\Reseller\WalletsController@oneOff'); - Route::get('wallets/{id}/receipts', 'API\V4\Reseller\WalletsController@receipts'); - Route::get('wallets/{id}/receipts/{receipt}', 'API\V4\Reseller\WalletsController@receiptDownload'); - Route::get('wallets/{id}/transactions', 'API\V4\Reseller\WalletsController@transactions'); - - Route::get('stats/chart/{chart}', 'API\V4\Reseller\StatsController@chart'); + Route::apiResource('domains', API\V4\Reseller\DomainsController::class); + Route::get('domains/{id}/skus', [API\V4\Reseller\SkusController::class, 'domainSkus']); + Route::post('domains/{id}/suspend', [API\V4\Reseller\DomainsController::class, 'suspend']); + Route::post('domains/{id}/unsuspend', [API\V4\Reseller\DomainsController::class, 'unsuspend']); + + Route::apiResource('groups', API\V4\Reseller\GroupsController::class); + Route::post('groups/{id}/suspend', [API\V4\Reseller\GroupsController::class, 'suspend']); + Route::post('groups/{id}/unsuspend', [API\V4\Reseller\GroupsController::class, 'unsuspend']); + + Route::apiResource('invitations', API\V4\Reseller\InvitationsController::class); + Route::post('invitations/{id}/resend', [API\V4\Reseller\InvitationsController::class, 'resend']); + + Route::post('payments', [API\V4\Reseller\PaymentsController::class, 'store']); + Route::get('payments/mandate', [API\V4\Reseller\PaymentsController::class, 'mandate']); + Route::post('payments/mandate', [API\V4\Reseller\PaymentsController::class, 'mandateCreate']); + Route::put('payments/mandate', [API\V4\Reseller\PaymentsController::class, 'mandateUpdate']); + Route::delete('payments/mandate', [API\V4\Reseller\PaymentsController::class, 'mandateDelete']); + Route::get('payments/methods', [API\V4\Reseller\PaymentsController::class, 'paymentMethods']); + Route::get('payments/pending', [API\V4\Reseller\PaymentsController::class, 'payments']); + Route::get('payments/has-pending', [API\V4\Reseller\PaymentsController::class, 'hasPayments']); + + Route::apiResource('resources', API\V4\Reseller\ResourcesController::class); + Route::apiResource('shared-folders', API\V4\Reseller\SharedFoldersController::class); + Route::apiResource('skus', API\V4\Reseller\SkusController::class); + + Route::apiResource('users', API\V4\Reseller\UsersController::class); + Route::get('users/{id}/discounts', [API\V4\Reseller\DiscountsController::class, 'userDiscounts']); + Route::post('users/{id}/reset2FA', [API\V4\Reseller\UsersController::class, 'reset2FA']); + Route::get('users/{id}/skus', [API\V4\Reseller\SkusController::class, 'userSkus']); + Route::post('users/{id}/skus/{sku}', [API\V4\Admin\UsersController::class, 'setSku']); + Route::post('users/{id}/suspend', [API\V4\Reseller\UsersController::class, 'suspend']); + Route::post('users/{id}/unsuspend', [API\V4\Reseller\UsersController::class, 'unsuspend']); + + Route::apiResource('wallets', API\V4\Reseller\WalletsController::class); + Route::post('wallets/{id}/one-off', [API\V4\Reseller\WalletsController::class, 'oneOff']); + Route::get('wallets/{id}/receipts', [API\V4\Reseller\WalletsController::class, 'receipts']); + Route::get('wallets/{id}/receipts/{receipt}', [API\V4\Reseller\WalletsController::class, 'receiptDownload']); + Route::get('wallets/{id}/transactions', [API\V4\Reseller\WalletsController::class, 'transactions']); + + Route::get('stats/chart/{chart}', [API\V4\Reseller\StatsController::class, 'chart']); } ); } diff --git a/src/routes/channels.php b/src/routes/channels.php --- a/src/routes/channels.php +++ b/src/routes/channels.php @@ -1,5 +1,7 @@ comment(Inspiring::quote()); + $this->comment(Inspiring::quote()); // @phpstan-ignore-line })->describe('Display an inspiring quote'); diff --git a/src/routes/web.php b/src/routes/web.php --- a/src/routes/web.php +++ b/src/routes/web.php @@ -1,5 +1,8 @@ \config('app.website_domain'), ], function () { - Route::get('content/page/{page}', 'ContentController@pageContent') + Route::get('content/page/{page}', Controllers\ContentController::class . '@pageContent') ->where('page', '(.*)'); - Route::get('content/faq/{page}', 'ContentController@faqContent') + Route::get('content/faq/{page}', Controllers\ContentController::class . '@faqContent') ->where('page', '(.*)'); Route::fallback( function () { // Return 404 for requests to the API end-points that do not exist if (strpos(request()->path(), 'api/') === 0) { - return \App\Http\Controllers\Controller::errorResponse(404); + return Controllers\Controller::errorResponse(404); } $env = \App\Utils::uiEnv(); diff --git a/src/routes/websocket.php b/src/routes/websocket.php deleted file mode 100644 --- a/src/routes/websocket.php +++ /dev/null @@ -1,37 +0,0 @@ -browse(function ($browser) { + $browser->visit('/domains')->on(new Home()); + }); + } + + /** * Test domain info page (non-existing domain id) */ public function testDomainInfo404(): void @@ -164,19 +175,6 @@ } /** - * Test domains list page (unauthenticated) - */ - public function testDomainListUnauth(): void - { - // Test that the page requires authentication - $this->browse(function ($browser) { - $browser->visit('/logout') - ->visit('/domains') - ->on(new Home()); - }); - } - - /** * Test domains list page * * @depends testDomainListUnauth @@ -245,7 +243,7 @@ $this->browse(function ($browser) { $browser->visit('/login') ->on(new Home()) - ->submitLogon('john@kolab.org', 'simple123') + ->submitLogon('john@kolab.org', 'simple123', true) ->visit('/domains') ->on(new DomainList()) ->assertSeeIn('.card-title button.btn-success', 'Create domain') diff --git a/src/tests/Browser/ErrorTest.php b/src/tests/Browser/ErrorTest.php --- a/src/tests/Browser/ErrorTest.php +++ b/src/tests/Browser/ErrorTest.php @@ -7,7 +7,6 @@ class ErrorTest extends TestCaseDusk { - /** * Test error 404 page on unknown route * diff --git a/src/tests/Browser/SharedFolderTest.php b/src/tests/Browser/SharedFolderTest.php --- a/src/tests/Browser/SharedFolderTest.php +++ b/src/tests/Browser/SharedFolderTest.php @@ -239,7 +239,7 @@ // Test creation/updating a mail folder with mail aliases $this->browse(function (Browser $browser) { $browser->on(new SharedFolderList()) - ->click('button.create-folder') + ->click('button.shared-folder-new') ->on(new SharedFolderInfo()) ->type('#name', 'Test Folder2') ->with(new ListInput('#aliases'), function (Browser $browser) { diff --git a/src/tests/Feature/Auth/SecondFactorTest.php b/src/tests/Feature/Auth/SecondFactorTest.php --- a/src/tests/Feature/Auth/SecondFactorTest.php +++ b/src/tests/Feature/Auth/SecondFactorTest.php @@ -10,7 +10,9 @@ class SecondFactorTest extends TestCase { - + /** + * {@inheritDoc} + */ public function setUp(): void { parent::setUp(); @@ -18,6 +20,9 @@ $this->deleteTestUser('entitlement-test@kolabnow.com'); } + /** + * {@inheritDoc} + */ public function tearDown(): void { $this->deleteTestUser('entitlement-test@kolabnow.com'); diff --git a/src/tests/Feature/AuthAttemptTest.php b/src/tests/Feature/AuthAttemptTest.php --- a/src/tests/Feature/AuthAttemptTest.php +++ b/src/tests/Feature/AuthAttemptTest.php @@ -7,7 +7,9 @@ class AuthAttemptTest extends TestCase { - + /** + * {@inheritDoc} + */ public function setUp(): void { parent::setUp(); @@ -15,6 +17,9 @@ $this->deleteTestUser('jane@kolabnow.com'); } + /** + * {@inheritDoc} + */ public function tearDown(): void { $this->deleteTestUser('jane@kolabnow.com'); diff --git a/src/tests/Feature/Console/Data/Import/LdifTest.php b/src/tests/Feature/Console/Data/Import/LdifTest.php --- a/src/tests/Feature/Console/Data/Import/LdifTest.php +++ b/src/tests/Feature/Console/Data/Import/LdifTest.php @@ -92,6 +92,7 @@ // Users $this->assertSame(2, $owner->users(false)->count()); + /** @var \App\User $user */ $user = $owner->users(false)->where('email', 'user@kolab3.com')->first(); // User settings @@ -112,6 +113,7 @@ ]); // Domains + /** @var \App\Domain[] $domains */ $domains = $owner->domains(false, false)->orderBy('namespace')->get(); $this->assertCount(2, $domains); @@ -124,6 +126,7 @@ $this->assertEntitlements($domains[1], ['domain-hosting']); // Shared folders + /** @var \App\SharedFolder[] $folders */ $folders = $owner->sharedFolders(false)->orderBy('email')->get(); $this->assertCount(2, $folders); @@ -144,6 +147,7 @@ ); // Groups + /** @var \App\Group[] $groups */ $groups = $owner->groups(false)->orderBy('email')->get(); $this->assertCount(1, $groups); @@ -153,6 +157,7 @@ $this->assertSame('["sender@gmail.com","-"]', $groups[0]->getSetting('sender_policy')); // Resources + /** @var \App\Resource[] $resources */ $resources = $owner->resources(false)->orderBy('email')->get(); $this->assertCount(1, $resources); diff --git a/src/tests/Feature/Console/Scalpel/TenantSetting/CreateCommandTest.php b/src/tests/Feature/Console/Scalpel/TenantSetting/CreateCommandTest.php --- a/src/tests/Feature/Console/Scalpel/TenantSetting/CreateCommandTest.php +++ b/src/tests/Feature/Console/Scalpel/TenantSetting/CreateCommandTest.php @@ -38,7 +38,7 @@ $setting = $tenant->settings()->where('key', 'test')->first(); - $this->assertSame('init', $setting->fresh()->value); + $this->assertSame('init', $setting->value); $this->assertSame('init', $tenant->fresh()->getSetting('test')); } } diff --git a/src/tests/Feature/Console/Scalpel/WalletSetting/CreateCommandTest.php b/src/tests/Feature/Console/Scalpel/WalletSetting/CreateCommandTest.php --- a/src/tests/Feature/Console/Scalpel/WalletSetting/CreateCommandTest.php +++ b/src/tests/Feature/Console/Scalpel/WalletSetting/CreateCommandTest.php @@ -17,7 +17,7 @@ $setting = $wallet->settings()->where('key', 'test')->first(); - $this->assertSame('init', $setting->fresh()->value); + $this->assertSame('init', $setting->value); $this->assertSame('init', $wallet->fresh()->getSetting('test')); } } diff --git a/src/tests/Feature/Controller/SupportTest.php b/src/tests/Feature/Controller/SupportTest.php --- a/src/tests/Feature/Controller/SupportTest.php +++ b/src/tests/Feature/Controller/SupportTest.php @@ -47,7 +47,7 @@ $this->assertCount(1, $json['errors']); $this->assertSame(['The email must be a valid email address.'], $json['errors']['email']); - $this->assertCount(0, $this->app->make('swift.transport')->driver()->messages()); + $this->assertCount(0, $this->getSentMessages()); // Valid input $post = [ @@ -66,17 +66,44 @@ $this->assertSame('success', $json['status']); $this->assertSame('Support request submitted successfully.', $json['message']); - $emails = $this->app->make('swift.transport')->driver()->messages(); + $emails = $this->getSentMessages(); - $expected_body = "ID: 1234567\nName: Username\nWorking email address: test@test.com\n" + $this->assertCount(1, $emails); + + $to = $emails[0]->getTo(); + $from = $emails[0]->getFrom(); + $replyTo = $emails[0]->getReplyTo(); + $expectedBody = "ID: 1234567\nName: Username\nWorking email address: test@test.com\n" . "Subject: Test summary\n\nTest body"; - $this->assertCount(1, $emails); + $this->assertCount(1, $to); + $this->assertCount(1, $from); + $this->assertCount(1, $replyTo); $this->assertSame('Test summary', $emails[0]->getSubject()); - $this->assertSame(['test@test.com' => 'Username'], $emails[0]->getFrom()); - $this->assertSame(['test@test.com' => 'Username'], $emails[0]->getReplyTo()); - $this->assertNull($emails[0]->getCc()); - $this->assertSame([$support_email => null], $emails[0]->getTo()); - $this->assertSame($expected_body, trim($emails[0]->getBody())); + $this->assertSame('test@test.com', $from[0]->getAddress()); + $this->assertSame('Username', $from[0]->getName()); + $this->assertSame('test@test.com', $replyTo[0]->getAddress()); + $this->assertSame('Username', $replyTo[0]->getName()); + $this->assertSame([], $emails[0]->getCc()); + $this->assertSame($support_email, $to[0]->getAddress()); + $this->assertSame('', $to[0]->getName()); + $this->assertSame($expectedBody, trim($emails[0]->getTextBody())); + $this->assertSame('', trim($emails[0]->getHtmlBody())); + } + + /** + * Get all messages that have been sent + * + * @return \Symfony\Component\Mime\Email[] + */ + protected function getSentMessages(): array + { + $transport = $this->app->make('mail.manager')->mailer()->getSymfonyTransport(); + + return $this->getObjectProperty($transport, 'messages') + ->map(function (\Symfony\Component\Mailer\SentMessage $item) { + return $item->getOriginalMessage(); + }) + ->all(); } } 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 @@ -736,6 +736,7 @@ $this->assertSame('John2', $user->getSetting('first_name')); $this->assertSame('Doe2', $user->getSetting('last_name')); $this->assertSame('TestOrg', $user->getSetting('organization')); + /** @var \App\UserAlias[] $aliases */ $aliases = $user->aliases()->orderBy('alias')->get(); $this->assertCount(2, $aliases); $this->assertSame('deleted@kolab.org', $aliases[0]->alias); 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 @@ -11,8 +11,6 @@ class PasswordResetEmailTest extends TestCase { - private $code; - /** * {@inheritDoc} * 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 @@ -11,7 +11,9 @@ class SkuTest extends TestCase { - + /** + * {@inheritDoc} + */ public function setUp(): void { parent::setUp(); @@ -19,6 +21,9 @@ $this->deleteTestUser('jane@kolabnow.com'); } + /** + * {@inheritDoc} + */ public function tearDown(): void { $this->deleteTestUser('jane@kolabnow.com'); diff --git a/src/tests/Feature/Stories/GreylistTest.php b/src/tests/Feature/Stories/GreylistTest.php --- a/src/tests/Feature/Stories/GreylistTest.php +++ b/src/tests/Feature/Stories/GreylistTest.php @@ -14,7 +14,6 @@ class GreylistTest extends TestCase { private $clientAddress; - private $requests = []; private $net; public function setUp(): void diff --git a/src/tests/Feature/TenantTest.php b/src/tests/Feature/TenantTest.php --- a/src/tests/Feature/TenantTest.php +++ b/src/tests/Feature/TenantTest.php @@ -8,7 +8,6 @@ class TenantTest extends TestCase { - /** * {@inheritDoc} */ diff --git a/src/tests/MailInterceptTrait.php b/src/tests/MailInterceptTrait.php deleted file mode 100644 --- a/src/tests/MailInterceptTrait.php +++ /dev/null @@ -1,79 +0,0 @@ -interceptMail(); - - Mail::send($mail); - - $message = $this->interceptedMail()->last(); - - // SwiftMailer does not have methods to get the bodies, we'll parse the message - list($plain, $html) = $this->extractMailBody($message->toString()); - - return [ - 'plain' => $plain, - 'html' => $html, - 'message' => $message, - ]; - } - - /** - * Simple message parser to extract plain and html body - * - * @param string $message Email message as string - * - * @return array Plain text and HTML body - */ - protected function extractMailBody(string $message): array - { - // Note that we're not supporting every message format, we only - // support what Laravel/SwiftMailer produces - // TODO: It may stop working if we start using attachments - $plain = ''; - $html = ''; - - if (preg_match('/[\s\t]boundary="([^"]+)"/', $message, $matches)) { - // multipart message assume plain and html parts - $split = preg_split('/--' . preg_quote($matches[1]) . '/', $message); - - list($plain_head, $plain) = explode("\r\n\r\n", $split[1], 2); - list($html_head, $html) = explode("\r\n\r\n", $split[2], 2); - - if (strpos($plain_head, 'Content-Transfer-Encoding: quoted-printable') !== false) { - $plain = quoted_printable_decode($plain); - } - - if (strpos($html_head, 'Content-Transfer-Encoding: quoted-printable') !== false) { - $html = quoted_printable_decode($html); - } - } else { - list($header, $html) = explode("\r\n\r\n", $message, 2); - if (strpos($header, 'Content-Transfer-Encoding: quoted-printable') !== false) { - $html = quoted_printable_decode($html); - } - } - - return [$plain, $html]; - } -} diff --git a/src/tests/TestCaseTrait.php b/src/tests/TestCaseTrait.php --- a/src/tests/TestCaseTrait.php +++ b/src/tests/TestCaseTrait.php @@ -495,7 +495,7 @@ * * @return mixed Method return. */ - protected function invokeMethod($object, $methodName, array $parameters = array()) + protected function invokeMethod($object, $methodName, array $parameters = []) { $reflection = new \ReflectionClass(get_class($object)); $method = $reflection->getMethod($methodName); @@ -504,6 +504,29 @@ return $method->invokeArgs($object, $parameters); } + /** + * Extract content of an email message. + * + * @param \Illuminate\Mail\Mailable $mail Mailable object + * + * @return array Parsed message data: + * - 'plain': Plain text body + * - 'html: HTML body + * - 'subject': Mail subject + */ + protected function renderMail(\Illuminate\Mail\Mailable $mail): array + { + $mail->build(); // @phpstan-ignore-line + + $result = $this->invokeMethod($mail, 'renderForAssertions'); + + return [ + 'plain' => $result[1], + 'html' => $result[0], + 'subject' => $mail->subject, + ]; + } + protected function setUpTest() { $this->userPassword = \App\Utils::generatePassphrase(); diff --git a/src/tests/Unit/Mail/DegradedAccountReminderTest.php b/src/tests/Unit/Mail/DegradedAccountReminderTest.php --- a/src/tests/Unit/Mail/DegradedAccountReminderTest.php +++ b/src/tests/Unit/Mail/DegradedAccountReminderTest.php @@ -5,13 +5,10 @@ use App\Mail\DegradedAccountReminder; use App\User; use App\Wallet; -use Tests\MailInterceptTrait; use Tests\TestCase; class DegradedAccountReminderTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -20,7 +17,7 @@ $user = $this->getTestUser('ned@kolab.org'); $wallet = $user->wallets->first(); - $mail = $this->fakeMail(new DegradedAccountReminder($wallet, $user)); + $mail = $this->renderMail(new DegradedAccountReminder($wallet, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -29,7 +26,7 @@ $dashboardLink = sprintf('%s', $dashboardUrl, $dashboardUrl); $appName = $user->tenant->title; - $this->assertMailSubject("$appName Reminder: Your account is free", $mail['message']); + $this->assertSame("$appName Reminder: Your account is free", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); diff --git a/src/tests/Unit/Mail/NegativeBalanceBeforeDeleteTest.php b/src/tests/Unit/Mail/NegativeBalanceBeforeDeleteTest.php --- a/src/tests/Unit/Mail/NegativeBalanceBeforeDeleteTest.php +++ b/src/tests/Unit/Mail/NegativeBalanceBeforeDeleteTest.php @@ -6,13 +6,10 @@ use App\Mail\NegativeBalanceBeforeDelete; use App\User; use App\Wallet; -use Tests\MailInterceptTrait; use Tests\TestCase; class NegativeBalanceBeforeDeleteTest extends TestCase { - use MailInterceptTrait; - /** * {@inheritDoc} */ @@ -49,7 +46,7 @@ 'app.support_url' => 'https://kolab.org/support', ]); - $mail = $this->fakeMail(new NegativeBalanceBeforeDelete($wallet, $user)); + $mail = $this->renderMail(new NegativeBalanceBeforeDelete($wallet, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -60,7 +57,7 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = $user->tenant->title; - $this->assertMailSubject("$appName Final Warning", $mail['message']); + $this->assertSame("$appName Final Warning", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); @@ -93,7 +90,7 @@ 'app.public_url' => 'https://test.org', ]); - $mail = $this->fakeMail(new NegativeBalanceBeforeDelete($wallet, $user)); + $mail = $this->renderMail(new NegativeBalanceBeforeDelete($wallet, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -103,7 +100,7 @@ $supportUrl = 'https://test.org/support'; $supportLink = sprintf('%s', $supportUrl, $supportUrl); - $this->assertMailSubject("{$tenant->title} Final Warning", $mail['message']); + $this->assertSame("{$tenant->title} Final Warning", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); diff --git a/src/tests/Unit/Mail/NegativeBalanceDegradedTest.php b/src/tests/Unit/Mail/NegativeBalanceDegradedTest.php --- a/src/tests/Unit/Mail/NegativeBalanceDegradedTest.php +++ b/src/tests/Unit/Mail/NegativeBalanceDegradedTest.php @@ -6,13 +6,10 @@ use App\Mail\NegativeBalanceDegraded; use App\User; use App\Wallet; -use Tests\MailInterceptTrait; use Tests\TestCase; class NegativeBalanceDegradedTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -27,7 +24,7 @@ 'app.support_url' => 'https://kolab.org/support', ]); - $mail = $this->fakeMail(new NegativeBalanceDegraded($wallet, $user)); + $mail = $this->renderMail(new NegativeBalanceDegraded($wallet, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -38,7 +35,7 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = $user->tenant->title; - $this->assertMailSubject("$appName Account Degraded", $mail['message']); + $this->assertSame("$appName Account Degraded", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); diff --git a/src/tests/Unit/Mail/NegativeBalanceReminderDegradeTest.php b/src/tests/Unit/Mail/NegativeBalanceReminderDegradeTest.php --- a/src/tests/Unit/Mail/NegativeBalanceReminderDegradeTest.php +++ b/src/tests/Unit/Mail/NegativeBalanceReminderDegradeTest.php @@ -6,13 +6,10 @@ use App\Mail\NegativeBalanceReminderDegrade; use App\User; use App\Wallet; -use Tests\MailInterceptTrait; use Tests\TestCase; class NegativeBalanceReminderDegradeTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -29,7 +26,7 @@ 'app.support_url' => 'https://kolab.org/support', ]); - $mail = $this->fakeMail(new NegativeBalanceReminderDegrade($wallet, $user)); + $mail = $this->renderMail(new NegativeBalanceReminderDegrade($wallet, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -40,7 +37,7 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = $user->tenant->title; - $this->assertMailSubject("$appName Payment Reminder", $mail['message']); + $this->assertSame("$appName Payment Reminder", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); diff --git a/src/tests/Unit/Mail/NegativeBalanceReminderTest.php b/src/tests/Unit/Mail/NegativeBalanceReminderTest.php --- a/src/tests/Unit/Mail/NegativeBalanceReminderTest.php +++ b/src/tests/Unit/Mail/NegativeBalanceReminderTest.php @@ -6,13 +6,10 @@ use App\Mail\NegativeBalanceReminder; use App\User; use App\Wallet; -use Tests\MailInterceptTrait; use Tests\TestCase; class NegativeBalanceReminderTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -29,7 +26,7 @@ 'app.support_url' => 'https://kolab.org/support', ]); - $mail = $this->fakeMail(new NegativeBalanceReminder($wallet, $user)); + $mail = $this->renderMail(new NegativeBalanceReminder($wallet, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -40,7 +37,7 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = $user->tenant->title; - $this->assertMailSubject("$appName Payment Reminder", $mail['message']); + $this->assertSame("$appName Payment Reminder", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); diff --git a/src/tests/Unit/Mail/NegativeBalanceSuspendedTest.php b/src/tests/Unit/Mail/NegativeBalanceSuspendedTest.php --- a/src/tests/Unit/Mail/NegativeBalanceSuspendedTest.php +++ b/src/tests/Unit/Mail/NegativeBalanceSuspendedTest.php @@ -6,13 +6,10 @@ use App\Mail\NegativeBalanceSuspended; use App\User; use App\Wallet; -use Tests\MailInterceptTrait; use Tests\TestCase; class NegativeBalanceSuspendedTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -29,7 +26,7 @@ 'app.support_url' => 'https://kolab.org/support', ]); - $mail = $this->fakeMail(new NegativeBalanceSuspended($wallet, $user)); + $mail = $this->renderMail(new NegativeBalanceSuspended($wallet, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -40,7 +37,7 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = $user->tenant->title; - $this->assertMailSubject("$appName Account Suspended", $mail['message']); + $this->assertSame("$appName Account Suspended", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); diff --git a/src/tests/Unit/Mail/NegativeBalanceTest.php b/src/tests/Unit/Mail/NegativeBalanceTest.php --- a/src/tests/Unit/Mail/NegativeBalanceTest.php +++ b/src/tests/Unit/Mail/NegativeBalanceTest.php @@ -5,13 +5,10 @@ use App\Mail\NegativeBalance; use App\User; use App\Wallet; -use Tests\MailInterceptTrait; use Tests\TestCase; class NegativeBalanceTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -24,7 +21,7 @@ 'app.support_url' => 'https://kolab.org/support', ]); - $mail = $this->fakeMail(new NegativeBalance($wallet, $user)); + $mail = $this->renderMail(new NegativeBalance($wallet, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -35,7 +32,7 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = \config('app.name'); - $this->assertMailSubject("$appName Payment Required", $mail['message']); + $this->assertSame("$appName Payment Required", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); diff --git a/src/tests/Unit/Mail/PasswordResetTest.php b/src/tests/Unit/Mail/PasswordResetTest.php --- a/src/tests/Unit/Mail/PasswordResetTest.php +++ b/src/tests/Unit/Mail/PasswordResetTest.php @@ -6,13 +6,10 @@ use App\User; use App\Utils; use App\VerificationCode; -use Tests\MailInterceptTrait; use Tests\TestCase; class PasswordResetTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -29,7 +26,7 @@ 'name' => 'User Name', ]); - $mail = $this->fakeMail(new PasswordReset($code)); + $mail = $this->renderMail(new PasswordReset($code)); $html = $mail['html']; $plain = $mail['plain']; @@ -38,7 +35,7 @@ $link = "$url"; $appName = \config('app.name'); - $this->assertMailSubject("$appName Password Reset", $mail['message']); + $this->assertSame("$appName Password Reset", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $link) > 0); diff --git a/src/tests/Unit/Mail/PaymentFailureTest.php b/src/tests/Unit/Mail/PaymentFailureTest.php --- a/src/tests/Unit/Mail/PaymentFailureTest.php +++ b/src/tests/Unit/Mail/PaymentFailureTest.php @@ -5,13 +5,10 @@ use App\Mail\PaymentFailure; use App\Payment; use App\User; -use Tests\MailInterceptTrait; use Tests\TestCase; class PaymentFailureTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -23,7 +20,7 @@ \config(['app.support_url' => 'https://kolab.org/support']); - $mail = $this->fakeMail(new PaymentFailure($payment, $user)); + $mail = $this->renderMail(new PaymentFailure($payment, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -34,7 +31,7 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = \config('app.name'); - $this->assertMailSubject("$appName Payment Failed", $mail['message']); + $this->assertSame("$appName Payment Failed", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); diff --git a/src/tests/Unit/Mail/PaymentMandateDisabledTest.php b/src/tests/Unit/Mail/PaymentMandateDisabledTest.php --- a/src/tests/Unit/Mail/PaymentMandateDisabledTest.php +++ b/src/tests/Unit/Mail/PaymentMandateDisabledTest.php @@ -5,13 +5,10 @@ use App\Mail\PaymentMandateDisabled; use App\Wallet; use App\User; -use Tests\MailInterceptTrait; use Tests\TestCase; class PaymentMandateDisabledTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -22,7 +19,7 @@ \config(['app.support_url' => 'https://kolab.org/support']); - $mail = $this->fakeMail(new PaymentMandateDisabled($wallet, $user)); + $mail = $this->renderMail(new PaymentMandateDisabled($wallet, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -33,7 +30,7 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = \config('app.name'); - $this->assertMailSubject("$appName Auto-payment Problem", $mail['message']); + $this->assertSame("$appName Auto-payment Problem", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); diff --git a/src/tests/Unit/Mail/PaymentSuccessTest.php b/src/tests/Unit/Mail/PaymentSuccessTest.php --- a/src/tests/Unit/Mail/PaymentSuccessTest.php +++ b/src/tests/Unit/Mail/PaymentSuccessTest.php @@ -5,13 +5,10 @@ use App\Mail\PaymentSuccess; use App\Payment; use App\User; -use Tests\MailInterceptTrait; use Tests\TestCase; class PaymentSuccessTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -23,7 +20,7 @@ \config(['app.support_url' => 'https://kolab.org/support']); - $mail = $this->fakeMail(new PaymentSuccess($payment, $user)); + $mail = $this->renderMail(new PaymentSuccess($payment, $user)); $html = $mail['html']; $plain = $mail['plain']; @@ -34,7 +31,7 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = \config('app.name'); - $this->assertMailSubject("$appName Payment Succeeded", $mail['message']); + $this->assertSame("$appName Payment Succeeded", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); diff --git a/src/tests/Unit/Mail/SignupInvitationTest.php b/src/tests/Unit/Mail/SignupInvitationTest.php --- a/src/tests/Unit/Mail/SignupInvitationTest.php +++ b/src/tests/Unit/Mail/SignupInvitationTest.php @@ -5,13 +5,10 @@ use App\Mail\SignupInvitation; use App\SignupInvitation as SI; use App\Utils; -use Tests\MailInterceptTrait; use Tests\TestCase; class SignupInvitationTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -22,7 +19,7 @@ 'email' => 'test@email', ]); - $mail = $this->fakeMail(new SignupInvitation($invitation)); + $mail = $this->renderMail(new SignupInvitation($invitation)); $html = $mail['html']; $plain = $mail['plain']; @@ -31,7 +28,7 @@ $link = "$url"; $appName = \config('app.name'); - $this->assertMailSubject("$appName Invitation", $mail['message']); + $this->assertSame("$appName Invitation", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $link) > 0); diff --git a/src/tests/Unit/Mail/SignupVerificationTest.php b/src/tests/Unit/Mail/SignupVerificationTest.php --- a/src/tests/Unit/Mail/SignupVerificationTest.php +++ b/src/tests/Unit/Mail/SignupVerificationTest.php @@ -5,13 +5,10 @@ use App\Mail\SignupVerification; use App\SignupCode; use App\Utils; -use Tests\MailInterceptTrait; use Tests\TestCase; class SignupVerificationTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -25,7 +22,7 @@ 'last_name' => 'Last', ]); - $mail = $this->fakeMail(new SignupVerification($code)); + $mail = $this->renderMail(new SignupVerification($code)); $html = $mail['html']; $plain = $mail['plain']; @@ -34,7 +31,7 @@ $link = "$url"; $appName = \config('app.name'); - $this->assertMailSubject("$appName Registration", $mail['message']); + $this->assertSame("$appName Registration", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $link) > 0); diff --git a/src/tests/Unit/Mail/SuspendedDebtorTest.php b/src/tests/Unit/Mail/SuspendedDebtorTest.php --- a/src/tests/Unit/Mail/SuspendedDebtorTest.php +++ b/src/tests/Unit/Mail/SuspendedDebtorTest.php @@ -4,13 +4,10 @@ use App\Mail\SuspendedDebtor; use App\User; -use Tests\MailInterceptTrait; use Tests\TestCase; class SuspendedDebtorTest extends TestCase { - use MailInterceptTrait; - /** * Test email content */ @@ -24,7 +21,7 @@ 'app.kb.account_delete' => 'https://kb.kolab.org/account-delete', ]); - $mail = $this->fakeMail(new SuspendedDebtor($user)); + $mail = $this->renderMail(new SuspendedDebtor($user)); $html = $mail['html']; $plain = $mail['plain']; @@ -39,7 +36,7 @@ $moreLink = sprintf('here', $moreUrl); $appName = \config('app.name'); - $this->assertMailSubject("$appName Account Suspended", $mail['message']); + $this->assertSame("$appName Account Suspended", $mail['subject']); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0);