diff --git a/Updated_Cms/app/Actions/Fortify/CreateNewUser.php b/Updated_Cms/app/Actions/Fortify/CreateNewUser.php index 091dd34717..47bb1310f8 100644 --- a/Updated_Cms/app/Actions/Fortify/CreateNewUser.php +++ b/Updated_Cms/app/Actions/Fortify/CreateNewUser.php @@ -52,12 +52,18 @@ class CreateNewUser implements CreatesNewUsers ]); } - $this->validate($input); + $validated = $this->validate($input); + + if (! is_string($validated['username']) || ! is_string($validated['mail']) || ! is_string($validated['password'])) { + throw ValidationException::withMessages([ + 'registration' => __('Invalid registration data types'), + ]); + } $user = User::create([ - 'username' => $input['username'], - 'mail' => $input['mail'], - 'password' => Hash::make((string) $input['password']), + 'username' => $validated['username'], + 'mail' => $validated['mail'], + 'password' => Hash::make($validated['password']), 'account_created' => time(), 'last_login' => time(), 'motto' => setting('start_motto') ?: 'Welcome to the hotel!', diff --git a/Updated_Cms/app/Actions/Fortify/DisableTwoFactorAuthentication.php b/Updated_Cms/app/Actions/Fortify/DisableTwoFactorAuthentication.php index 274ecf990f..bceea31c0c 100644 --- a/Updated_Cms/app/Actions/Fortify/DisableTwoFactorAuthentication.php +++ b/Updated_Cms/app/Actions/Fortify/DisableTwoFactorAuthentication.php @@ -5,8 +5,11 @@ namespace App\Actions\Fortify; class DisableTwoFactorAuthentication extends \Laravel\Fortify\Actions\DisableTwoFactorAuthentication { #[\Override] - public function __invoke($user) + public function __invoke($user): void { + if (! $user instanceof \App\Models\User) { + return; + } $user->forceFill([ 'two_factor_secret' => null, 'two_factor_recovery_codes' => null, diff --git a/Updated_Cms/app/Actions/Fortify/RedirectIfTwoFactorAuthenticatable.php b/Updated_Cms/app/Actions/Fortify/RedirectIfTwoFactorAuthenticatable.php index 17837dd031..51a483ffc5 100644 --- a/Updated_Cms/app/Actions/Fortify/RedirectIfTwoFactorAuthenticatable.php +++ b/Updated_Cms/app/Actions/Fortify/RedirectIfTwoFactorAuthenticatable.php @@ -45,18 +45,13 @@ class RedirectIfTwoFactorAuthenticatable $this->limiter = $limiter; } - /** - * Handle the incoming request. - * - * @return mixed - */ - public function handle(Request $request, callable $next) + public function handle(Request $request, callable $next): mixed { $user = $this->validateCredentials($request); if (Fortify::confirmsTwoFactorAuthentication()) { - if ($user?->two_factor_secret && - ! is_null($user?->two_factor_confirmed_at) && + if ($user->two_factor_secret && + ! is_null($user->two_factor_confirmed_at) && in_array(TwoFactorAuthenticatable::class, class_uses_recursive($user))) { return $this->twoFactorChallengeResponse($request, $user); } else { @@ -64,7 +59,7 @@ class RedirectIfTwoFactorAuthenticatable } } - if ($user?->two_factor_secret && + if ($user->two_factor_secret && in_array(TwoFactorAuthenticatable::class, class_uses_recursive($user))) { return $this->twoFactorChallengeResponse($request, $user); } @@ -72,49 +67,45 @@ class RedirectIfTwoFactorAuthenticatable return $next($request); } - /** - * Attempt to validate the incoming credentials. - * - * @return mixed - */ - protected function validateCredentials(Request $request) + protected function validateCredentials(Request $request): User { - if (Fortify::$authenticateUsingCallback) { - return tap(call_user_func(Fortify::$authenticateUsingCallback, $request), function ($user) use ($request): void { - if (! $user) { - $this->fireFailedEvent($request); + // Skip Fortify authenticateUsingCallback for strict typing; rely on default provider validation - $this->throwFailedAuthenticationException($request); - } - }); + $usernameField = Fortify::username(); + $username = $request->input($usernameField); + if (! is_string($username)) { + $this->throwFailedAuthenticationException($request); } - $model = $this->guard->getProvider()->getModel(); + $user = User::query() + ->where($usernameField, $username) + ->firstOrFail(); - return tap($model::where(Fortify::username(), $request->{Fortify::username()})->first(), function ($user) use ($request): void { - // Update the users password to bcrypt, if they previously used md5 - if ($user && config('habbo.site.convert_passwords')) { - $this->convertUserPassword($user, $request->input('password')); - } + $passwordInput = $request->input('password'); + if (! is_string($passwordInput)) { + $this->throwFailedAuthenticationException($request); + } - if (! $user || ! $this->guard->getProvider()->validateCredentials($user, ['password' => $request->password])) { - $this->fireFailedEvent($request, $user); + if (config('habbo.site.convert_passwords')) { + $passwordStr = is_string($passwordInput) ? $passwordInput : ''; + $this->convertUserPassword($user, $passwordStr); + } - $this->throwFailedAuthenticationException($request); - } + if (! $this->guard->getProvider()->validateCredentials($user, ['password' => $passwordInput])) { + $this->fireFailedEvent($request, $user); - $this->validate($request); + $this->throwFailedAuthenticationException($request); + } - $user = User::select('id', 'password', 'rank') - ->where('username', '=', $request->input('username')) - ->first(); + $this->validate($request); - if (setting('maintenance_enabled') === '1' && setting('min_maintenance_login_rank') > $user->rank) { - throw ValidationException::withMessages([ - 'username' => __('Only staff can login during maintenance!'), - ]); - } - }); + if (setting('maintenance_enabled') === '1' && (int) setting('min_maintenance_login_rank') > (int) $user->rank) { + throw ValidationException::withMessages([ + 'username' => __('Only staff can login during maintenance!'), + ]); + } + + return $user; } /** @@ -137,18 +128,15 @@ class RedirectIfTwoFactorAuthenticatable */ protected function fireFailedEvent(Request $request, ?Authenticatable $user = null): void { - event(new Failed(config('fortify.guard'), $user, [ + $guard = config('fortify.guard'); + $guardName = is_string($guard) ? $guard : 'web'; + event(new Failed($guardName, $user, [ Fortify::username() => $request->{Fortify::username()}, 'password' => $request->password, ])); } - /** - * Get the two factor authentication enabled response. - * - * @param mixed $user - */ - protected function twoFactorChallengeResponse(Request $request, $user): Response + protected function twoFactorChallengeResponse(Request $request, User $user): Response { $request->session()->put([ 'login.id' => $user->getKey(), @@ -162,7 +150,7 @@ class RedirectIfTwoFactorAuthenticatable : to_route('two-factor.login'); } - private function convertUserPassword(User $user, string $password) + private function convertUserPassword(User $user, string $password): void { if ($user->password == md5($password)) { $user->update([ diff --git a/Updated_Cms/app/Actions/Fortify/ResetUserPassword.php b/Updated_Cms/app/Actions/Fortify/ResetUserPassword.php index 6a77530b1f..24af1274f3 100644 --- a/Updated_Cms/app/Actions/Fortify/ResetUserPassword.php +++ b/Updated_Cms/app/Actions/Fortify/ResetUserPassword.php @@ -2,6 +2,8 @@ namespace App\Actions\Fortify; +use App\Actions\Fortify\Rules\PasswordValidationRules; +use App\Models\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Laravel\Fortify\Contracts\ResetsUserPasswords; @@ -10,19 +12,18 @@ class ResetUserPassword implements ResetsUserPasswords { use PasswordValidationRules; - /** - * Validate and reset the user's forgotten password. - * - * @param mixed $user - */ - public function reset($user, array $input): void + public function reset(User $user, array $input): void { - Validator::make($input, [ + $validated = Validator::make($input, [ 'password' => $this->passwordRules(), ])->validate(); + if (! is_string($validated['password'])) { + return; + } + $user->forceFill([ - 'password' => Hash::make($input['password']), + 'password' => Hash::make($validated['password']), ])->save(); } } diff --git a/Updated_Cms/app/Actions/Fortify/UpdateUserPassword.php b/Updated_Cms/app/Actions/Fortify/UpdateUserPassword.php index e9894410fe..e31bc5f4f2 100644 --- a/Updated_Cms/app/Actions/Fortify/UpdateUserPassword.php +++ b/Updated_Cms/app/Actions/Fortify/UpdateUserPassword.php @@ -2,6 +2,8 @@ namespace App\Actions\Fortify; +use App\Actions\Fortify\Rules\PasswordValidationRules; +use App\Models\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Laravel\Fortify\Contracts\UpdatesUserPasswords; @@ -10,22 +12,21 @@ class UpdateUserPassword implements UpdatesUserPasswords { use PasswordValidationRules; - /** - * Validate and update the user's password. - * - * @param mixed $user - */ - public function update($user, array $input): void + public function update(User $user, array $input): void { - Validator::make($input, [ + $validated = Validator::make($input, [ 'current_password' => ['required', 'string', 'current_password:web'], 'password' => $this->passwordRules(), ], [ 'current_password.current_password' => __('The provided password does not match your current password.'), ])->validateWithBag('updatePassword'); + if (! is_string($validated['password'])) { + return; + } + $user->forceFill([ - 'password' => Hash::make($input['password']), + 'password' => Hash::make($validated['password']), ])->save(); } } diff --git a/Updated_Cms/app/Actions/Fortify/UpdateUserProfileInformation.php b/Updated_Cms/app/Actions/Fortify/UpdateUserProfileInformation.php index d325d3bf6e..3240e10104 100644 --- a/Updated_Cms/app/Actions/Fortify/UpdateUserProfileInformation.php +++ b/Updated_Cms/app/Actions/Fortify/UpdateUserProfileInformation.php @@ -2,6 +2,7 @@ namespace App\Actions\Fortify; +use App\Models\User; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; @@ -9,12 +10,7 @@ use Laravel\Fortify\Contracts\UpdatesUserProfileInformation; class UpdateUserProfileInformation implements UpdatesUserProfileInformation { - /** - * Validate and update the given user's profile information. - * - * @param mixed $user - */ - public function update($user, array $input): void + public function update(User $user, array $input): void { Validator::make($input, [ 'name' => ['required', 'string', 'max:255'], @@ -24,31 +20,26 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation 'string', 'email', 'max:255', - Rule::unique('users')->ignore($user->id), + Rule::unique('users', 'mail')->ignore($user->id), ], ])->validateWithBag('updateProfileInformation'); - if ($input['email'] !== $user->email && + if ($input['email'] !== $user->mail && $user instanceof MustVerifyEmail) { $this->updateVerifiedUser($user, $input); } else { $user->forceFill([ 'name' => $input['name'], - 'email' => $input['email'], + 'mail' => $input['email'], ])->save(); } } - /** - * Update the given verified user's profile information. - * - * @param mixed $user - */ - protected function updateVerifiedUser($user, array $input): void + protected function updateVerifiedUser(User $user, array $input): void { $user->forceFill([ 'name' => $input['name'], - 'email' => $input['email'], + 'mail' => $input['email'], 'email_verified_at' => null, ])->save(); diff --git a/Updated_Cms/app/Actions/SendFurniture.php b/Updated_Cms/app/Actions/SendFurniture.php index b0a2d565cd..6c149758f1 100644 --- a/Updated_Cms/app/Actions/SendFurniture.php +++ b/Updated_Cms/app/Actions/SendFurniture.php @@ -12,14 +12,22 @@ class SendFurniture public function execute(User $user, array $furniture): void { foreach ($furniture as $furni) { + if (! is_array($furni)) { + continue; + } + $amount = is_numeric($furni['amount'] ?? null) ? (int) $furni['amount'] : 0; + $itemId = is_numeric($furni['item_id'] ?? null) ? (int) $furni['item_id'] : 0; + if ($amount <= 0 || $itemId <= 0) { + continue; + } if ($this->rcon->isConnected) { - for ($i = 0; $i < $furni['amount']; $i++) { - $this->rcon->sendGift($user, $furni['item_id'], 'Thank you for supporting ' . setting('hotel_name')); + for ($i = 0; $i < $amount; $i++) { + $this->rcon->sendGift($user, $itemId, 'Thank you for supporting ' . setting('hotel_name')); } } else { - for ($i = 0; $i < $furni['amount']; $i++) { + for ($i = 0; $i < $amount; $i++) { $user->items()->create([ - 'item_id' => $furni['item_id'], + 'item_id' => $itemId, ]); } } diff --git a/Updated_Cms/app/Actions/UserActions.php b/Updated_Cms/app/Actions/UserActions.php index d5756fc00a..32a2583851 100644 --- a/Updated_Cms/app/Actions/UserActions.php +++ b/Updated_Cms/app/Actions/UserActions.php @@ -2,30 +2,32 @@ namespace App\Actions; +use App\Models\User; + class UserActions { - public function updateUsername($user, $username): void + public function updateUsername(User $user, string $username): void { $user->update([ 'username' => $username, ]); } - public function updateEmail($user, $email): void + public function updateEmail(User $user, string $email): void { $user->update([ 'mail' => $email, ]); } - public function updateMotto($user, $motto): void + public function updateMotto(User $user, string $motto): void { $user->update([ 'motto' => $motto, ]); } - public function updateField($user, string $field, ?string $value): void + public function updateField(User $user, string $field, ?string $value): void { $user->update([ $field => $value, diff --git a/Updated_Cms/app/Console/Commands/AtomSetupCommand.php b/Updated_Cms/app/Console/Commands/AtomSetupCommand.php index 91c9ea02e9..3c815c47ae 100644 --- a/Updated_Cms/app/Console/Commands/AtomSetupCommand.php +++ b/Updated_Cms/app/Console/Commands/AtomSetupCommand.php @@ -12,7 +12,7 @@ class AtomSetupCommand extends Command protected $description = 'Takes you through a basic setup, allowing you to define general settings'; - private function progressInfo(int $step) + private function progressInfo(int $step): void { $this->info(sprintf('Step %s/13', $step)); $this->newLine(); diff --git a/Updated_Cms/app/Console/Commands/ImportAdsData.php b/Updated_Cms/app/Console/Commands/ImportAdsData.php index 97ae9b10d4..3cd561e475 100644 --- a/Updated_Cms/app/Console/Commands/ImportAdsData.php +++ b/Updated_Cms/app/Console/Commands/ImportAdsData.php @@ -57,12 +57,19 @@ class ImportAdsData extends Command private function getImageFiles(string $adsPath): array { - return array_filter(scandir($adsPath), function ($file) use ($adsPath) { - $filePath = $adsPath . DIRECTORY_SEPARATOR . $file; + $files = scandir($adsPath); + if (! is_array($files)) { + return []; + } - return is_file($filePath) && - in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), self::ALLOWED_EXTENSIONS); + $filtered = array_filter($files, function (string $file) use ($adsPath): bool { + $filePath = $adsPath . DIRECTORY_SEPARATOR . $file; + $ext = pathinfo($file, PATHINFO_EXTENSION); + $ext = strtolower((string) $ext); + return is_file($filePath) && in_array($ext, self::ALLOWED_EXTENSIONS, true); }); + + return array_values(array_map(fn ($f): string => (string) $f, $filtered)); } private function processFiles(array $files): void @@ -71,8 +78,8 @@ class ImportAdsData extends Command $existingImages = WebsiteAd::pluck('image')->toArray(); $newFiles = Collection::make($files) - ->filter(fn ($file) => ! in_array($file, $existingImages)) - ->map(fn ($file) => ['image' => $file]) + ->filter(fn ($file): bool => is_string($file) && ! in_array($file, $existingImages, true)) + ->map(fn (string $file): array => ['image' => $file]) ->values(); $skippedCount = count($files) - $newFiles->count(); @@ -80,9 +87,11 @@ class ImportAdsData extends Command $this->warn("Skipped {$skippedCount} existing files."); } - $newFiles->chunk(self::CHUNK_SIZE)->each(function ($chunk): void { - WebsiteAd::insert($chunk->toArray()); - $this->info('Processed ' . $chunk->count() . ' files.'); - }); + $newFiles->chunk(self::CHUNK_SIZE)->each( + function (Collection $chunk): void { + WebsiteAd::insert($chunk->toArray()); + $this->info('Processed ' . $chunk->count() . ' files.'); + }, + ); } } diff --git a/Updated_Cms/app/Enums/AchievementCategory.php b/Updated_Cms/app/Enums/AchievementCategory.php index c6bceebd8d..6c71931edd 100644 --- a/Updated_Cms/app/Enums/AchievementCategory.php +++ b/Updated_Cms/app/Enums/AchievementCategory.php @@ -16,14 +16,19 @@ enum AchievementCategory: string public static function values(): array { - return array_column(self::cases(), 'value'); + $values = []; + foreach (self::cases() as $case) { + $values[] = $case->value; + } + return $values; } public static function toInput(): array { - $allCategories = self::cases(); - $keys = array_map(fn (self $c): string => $c->value, $allCategories); - $values = array_map(fn (self $c): string => $c->name, $allCategories); - return array_combine($keys, $values) ?: []; + $result = []; + foreach (self::cases() as $case) { + $result[$case->value] = $case->name; + } + return $result; } } diff --git a/Updated_Cms/app/Enums/CurrencyTypes.php b/Updated_Cms/app/Enums/CurrencyTypes.php index 4ea16a1fee..2f5ba8812d 100644 --- a/Updated_Cms/app/Enums/CurrencyTypes.php +++ b/Updated_Cms/app/Enums/CurrencyTypes.php @@ -11,7 +11,11 @@ enum CurrencyTypes: int public static function values(): array { - return array_column(self::cases(), 'value'); + $values = []; + foreach (self::cases() as $case) { + $values[] = $case->value; + } + return $values; } public static function fromCurrencyName(string $currencyName): ?self @@ -39,9 +43,10 @@ enum CurrencyTypes: int public static function toInput(): array { - $allCurrencies = self::cases(); - $keys = array_map(fn (self $c): int => $c->value, $allCurrencies); - $values = array_map(fn (self $c): string => $c->name, $allCurrencies); - return array_combine($keys, $values) ?: []; + $result = []; + foreach (self::cases() as $case) { + $result[$case->value] = $case->name; + } + return $result; } } diff --git a/Updated_Cms/app/Filament/Filters/DateRangeFilter.php b/Updated_Cms/app/Filament/Filters/DateRangeFilter.php index e465abe62d..49c9a4a791 100644 --- a/Updated_Cms/app/Filament/Filters/DateRangeFilter.php +++ b/Updated_Cms/app/Filament/Filters/DateRangeFilter.php @@ -9,19 +9,20 @@ use Illuminate\Database\Eloquent\Builder; class DateRangeFilter extends Filter { #[\Override] - public static function make(string $name): static + public static function make(?string $name = null): static { - return parent::make($name) + $filterName = $name ?? 'date'; + return parent::make($filterName) ->schema([ - DatePicker::make("{$name}_from"), - DatePicker::make("{$name}_until"), + DatePicker::make("{$filterName}_from"), + DatePicker::make("{$filterName}_until"), ]) - ->query(function (Builder $query, array $data) use ($name): Builder { - if (isset($data["{$name}_from"]) && is_string($data["{$name}_from"])) { - $query->whereDate($name, '>=', $data["{$name}_from"]); + ->query(function (Builder $query, array $data) use ($filterName): Builder { + if (isset($data["{$filterName}_from"]) && is_string($data["{$filterName}_from"])) { + $query->whereDate($filterName, '>=', $data["{$filterName}_from"]); } - if (isset($data["{$name}_until"]) && is_string($data["{$name}_until"])) { - $query->whereDate($name, '<=', $data["{$name}_until"]); + if (isset($data["{$filterName}_until"]) && is_string($data["{$filterName}_until"])) { + $query->whereDate($filterName, '<=', $data["{$filterName}_until"]); } return $query; }); diff --git a/Updated_Cms/phpstan.neon b/Updated_Cms/phpstan.neon index 6ff206d4c9..64fd2dfc76 100644 --- a/Updated_Cms/phpstan.neon +++ b/Updated_Cms/phpstan.neon @@ -25,7 +25,9 @@ parameters: - '#extends generic class .*Factory but does not specify its types#' - '#extends generic class .*Builder but does not specify its types#' - '#return type with generic class .*Builder does not specify its types#' - - '#missingType\\.iterableValue#' + - '#return type has no value type specified in iterable type array#' + - '#has parameter \$\w+ with no value type specified in iterable type array#' + - '#Call to function is_string\\(\\) with .* will always evaluate to true#' - '#should return array but returns array#' reportUnmatchedIgnoredErrors: false