From e39541a6df751ac3b35f9dcb176844d26b9b77e4 Mon Sep 17 00:00:00 2001 From: kiritokatklian Date: Sun, 2 May 2021 23:15:32 +0200 Subject: [PATCH 1/5] Playing around with PhpStorm's code tab. Has some good features. --- app/Models/User.php | 136 ++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 81 deletions(-) diff --git a/app/Models/User.php b/app/Models/User.php index c537db94d..46ea210f5 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -9,10 +9,10 @@ use App\Traits\HeartActionTrait; use App\Traits\KuroSearchTrait; use App\Traits\MediaLibraryExtensionTrait; -use App\Traits\Web\Auth\TwoFactorAuthenticatable; use App\Traits\User\HasBannerImage; use App\Traits\User\HasProfileImage; use App\Traits\VoteActionTrait; +use App\Traits\Web\Auth\TwoFactorAuthenticatable; use Carbon\Carbon; use Cog\Contracts\Love\Reacterable\Models\Reacterable as ReacterableContract; use Cog\Laravel\Love\Reacterable\Models\Traits\Reacterable; @@ -25,7 +25,6 @@ use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; use Ramsey\Uuid\Uuid; use RuntimeException; @@ -171,8 +170,7 @@ public function getActivityStatus(): UserActivityStatus // Seen within the last 5 minutes if ($session->last_activity_at >= now()->subMinutes(5)) { return UserActivityStatus::Online(); - } - // Seen within the last 15 minutes + } // Seen within the last 15 minutes else if ($session->last_activity_at >= now()->subMinutes(15)) { return UserActivityStatus::SeenRecently(); } @@ -180,6 +178,16 @@ public function getActivityStatus(): UserActivityStatus return UserActivityStatus::Offline(); } + /** + * Returns the associated sessions for the user + * + * @return HasMany + */ + function sessions(): HasMany + { + return $this->hasMany(Session::class); + } + /** * Registers the media collections for the model. */ @@ -204,16 +212,6 @@ function favoriteAnime(): BelongsToMany return $this->belongsToMany(Anime::class, UserFavoriteAnime::class, 'user_id', 'anime_id'); } - /** - * Returns the Anime that the user has added to their reminders. - * - * @return BelongsToMany - */ - function reminderAnime(): BelongsToMany - { - return $this->belongsToMany(Anime::class, UserReminderAnime::class, 'user_id', 'anime_id'); - } - /** * Returns the User-Reminder-Anime relationship for the user. * @@ -240,7 +238,7 @@ function getCalendar(): string // Retrieve or save cached result return Cache::remember($cacheKey, self::CACHE_KEY_CALENDAR_SECONDS, function() use ($animes) { $appName = Env('APP_NAME'); - $productIdentifier = '-//Kurozora B.V.//' . $appName . '//'. strtoupper(config('app.locale')); + $productIdentifier = '-//Kurozora B.V.//' . $appName . '//' . strtoupper(config('app.locale')); $calendar = Calendar::create(UserReminderAnime::CAL_NAME); $calendar->description(UserReminderAnime::CAL_DESCRIPTION) @@ -256,10 +254,10 @@ function getCalendar(): string $endDate = Carbon::now()->endOfWeek()->addWeeks(2); $whereBetween = [$startDate, $endDate]; - foreach($animes as $anime) { + foreach ($animes as $anime) { $episodes = $anime->getEpisodes($whereBetween); - foreach($episodes as $episode) { + foreach ($episodes as $episode) { $uniqueIdentifier = Uuid::uuid4() . '@kurozora.app'; $eventName = $anime->title . ' Episode ' . $episode->number; $startsAt = $episode->first_aired->setTimezone('Asia/Tokyo'); @@ -307,26 +305,25 @@ function getCalendar(): string } /** - * Returns a boolean indicating whether the user has the given anime in their library. - * - * @param Anime $anime The anime to be searched for in the user's library. + * Returns the Anime that the user has added to their reminders. * - * @return bool + * @return BelongsToMany */ - function isTracking(Anime $anime): bool + function reminderAnime(): BelongsToMany { - return $this->library()->where('anime_id', $anime->id)->exists(); + return $this->belongsToMany(Anime::class, UserReminderAnime::class, 'user_id', 'anime_id'); } /** - * Returns the Anime that the user is moderating. + * Returns a boolean indicating whether the user has the given anime in their library. * - * @return BelongsToMany + * @param Anime $anime The anime to be searched for in the user's library. + * + * @return bool */ - function moderatingAnime(): BelongsToMany + function isTracking(Anime $anime): bool { - return $this->belongsToMany(Anime::class, AnimeModerator::class, 'user_id', 'anime_id') - ->withPivot('created_at'); + return $this->library()->where('anime_id', $anime->id)->exists(); } /** @@ -341,13 +338,14 @@ function library(): BelongsToMany } /** - * Returns the watched Episode items. + * Returns the Anime that the user is moderating. * * @return BelongsToMany */ - function watchedAnimeEpisodes(): BelongsToMany + function moderatingAnime(): BelongsToMany { - return $this->belongsToMany(AnimeEpisode::class, UserWatchedEpisode::class, 'user_id', 'episode_id'); + return $this->belongsToMany(Anime::class, AnimeModerator::class, 'user_id', 'anime_id') + ->withPivot('created_at'); } /** @@ -363,23 +361,23 @@ function hasWatched(AnimeEpisode $episode): bool } /** - * Returns the associated badges for the user + * Returns the watched Episode items. * * @return BelongsToMany */ - function badges(): BelongsToMany + function watchedAnimeEpisodes(): BelongsToMany { - return $this->belongsToMany(Badge::class, UserBadge::class, 'user_id', 'badge_id'); + return $this->belongsToMany(AnimeEpisode::class, UserWatchedEpisode::class, 'user_id', 'episode_id'); } /** - * Returns the associated sessions for the user + * Returns the associated badges for the user * - * @return HasMany + * @return BelongsToMany */ - function sessions(): HasMany + function badges(): BelongsToMany { - return $this->hasMany(Session::class); + return $this->belongsToMany(Badge::class, UserBadge::class, 'user_id', 'badge_id'); } /** @@ -397,7 +395,7 @@ function sessions(): HasMany * @param array $options * @return Session */ - function createSession($options = []): Session + function createSession(array $options = []): Session { $options = new OptionsBag($options); @@ -485,10 +483,9 @@ public function getBadges(): array // Retrieve or save cached result return Cache::remember($cacheKey, self::CACHE_KEY_BADGES_SECONDS, function () { - return Badge:: - join(UserBadge::TABLE_NAME, function ($join) { - $join->on(UserBadge::TABLE_NAME . '.badge_id', '=', Badge::TABLE_NAME . '.id'); - }) + return Badge::join(UserBadge::TABLE_NAME, function ($join) { + $join->on(UserBadge::TABLE_NAME . '.badge_id', '=', Badge::TABLE_NAME . '.id'); + }) ->where([ [UserBadge::TABLE_NAME . '.user_id', '=', $this->id] ]) @@ -496,16 +493,6 @@ public function getBadges(): array }); } - /** - * Get the user's followers - * - * @return BelongsToMany - */ - public function followers(): BelongsToMany - { - return $this->belongsToMany(User::class, UserFollow::class, 'following_user_id', 'user_id'); - } - /** * Returns the amount of followers the user has * @@ -523,13 +510,13 @@ public function getFollowerCount(): int } /** - * Get the user's following users. + * Get the user's followers * * @return BelongsToMany */ - public function following(): BelongsToMany + public function followers(): BelongsToMany { - return $this->belongsToMany(User::class, UserFollow::class, 'user_id', 'following_user_id'); + return $this->belongsToMany(User::class, UserFollow::class, 'following_user_id', 'user_id'); } /** @@ -548,6 +535,16 @@ public function getFollowingCount(): int }); } + /** + * Get the user's following users. + * + * @return BelongsToMany + */ + public function following(): BelongsToMany + { + return $this->belongsToMany(User::class, UserFollow::class, 'user_id', 'following_user_id'); + } + /** * Returns the total amount of reputation the user has * @@ -563,33 +560,10 @@ public function getReputationCount(): int $foundRep = UserReputation::where('given_user_id', $this->id)->sum('amount'); if ($foundRep === null) return 0; - return (int) $foundRep; + return (int)$foundRep; }); - return (int) $repCount; - } - - /** - * Generates a password hash from a raw string - * - * @param $rawPass - * @return string - */ - public static function hashPass($rawPass): string - { - return Hash::make($rawPass); - } - - /** - * Compares a raw password with a password hash - * - * @param $rawPass - * @param $hash - * @return bool - */ - public static function checkPassHash($rawPass, $hash): bool - { - return Hash::check($rawPass, $hash); + return (int)$repCount; } /** From 4e51c219a0eec8ea93c5b41b5017a16082c39995 Mon Sep 17 00:00:00 2001 From: kiritokatklian Date: Sun, 2 May 2021 23:18:24 +0200 Subject: [PATCH 2/5] [Refactor] Use Hash Directly - Removed the hashPass and checkPassHash methods from User model as they are unnecessary. - Replaced hashPass and checkPass with the default Hash::make and Hash::check methods --- .../Controllers/Auth/SignInWithAppleController.php | 3 ++- app/Http/Controllers/RegistrationController.php | 3 ++- app/Http/Controllers/SessionController.php | 12 ++++++++---- app/Http/Controllers/Web/SignUpUserController.php | 3 ++- app/Mail/ResetPassword.php | 11 +++++++++++ tests/Traits/ProvidesTestUser.php | 9 +++++---- 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/Auth/SignInWithAppleController.php b/app/Http/Controllers/Auth/SignInWithAppleController.php index 692eb03dc..c46c25dbc 100644 --- a/app/Http/Controllers/Auth/SignInWithAppleController.php +++ b/app/Http/Controllers/Auth/SignInWithAppleController.php @@ -10,6 +10,7 @@ use App\Http\Resources\UserResource; use App\Models\User; use Exception; +use Hash; use Illuminate\Http\JsonResponse; use Illuminate\Support\Str; use Laravel\Nova\Exceptions\AuthenticationException; @@ -146,7 +147,7 @@ protected function signUpUser(JWTPayload $payload): ?User [ 'email' => $payload->get('email'), 'siwa_id' => $payload->get('sub'), - 'password' => User::hashPass(Str::random(30)), + 'password' => Hash::make(Str::random(30)), 'settings' => [ 'can_change_username' => true, 'tv_rating' => -1 diff --git a/app/Http/Controllers/RegistrationController.php b/app/Http/Controllers/RegistrationController.php index f965c3177..a9c1dccc2 100644 --- a/app/Http/Controllers/RegistrationController.php +++ b/app/Http/Controllers/RegistrationController.php @@ -5,6 +5,7 @@ use App\Helpers\JSONResult; use App\Http\Requests\Web\SignUpRequest; use App\Models\User; +use Hash; use Illuminate\Auth\Events\Registered; use Illuminate\Http\JsonResponse; use Spatie\MediaLibrary\MediaCollections\Exceptions\FileDoesNotExist; @@ -29,7 +30,7 @@ public function signUp(SignUpRequest $request): JsonResponse $newUser = User::create([ 'username' => $data['username'], 'email' => $data['email'], - 'password' => User::hashPass($data['password']), + 'password' => Hash::make($data['password']), 'settings' => [ 'can_change_username' => false, 'tv_rating' => -1, diff --git a/app/Http/Controllers/SessionController.php b/app/Http/Controllers/SessionController.php index b39ac4978..aad8463c1 100644 --- a/app/Http/Controllers/SessionController.php +++ b/app/Http/Controllers/SessionController.php @@ -12,6 +12,7 @@ use App\Models\Session; use App\Models\User; use Exception; +use Hash; use Illuminate\Http\JsonResponse; use Laravel\Nova\Exceptions\AuthenticationException; use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; @@ -31,14 +32,15 @@ public function create(CreateSessionRequest $request): JsonResponse $data = $request->validated(); // Check if the request IP is not banned from logging in - if (!LoginAttempt::isIPAllowedToLogin($request->ip())) + if (!LoginAttempt::isIPAllowedToLogin($request->ip())) { throw new TooManyRequestsHttpException(300, 'You have failed to login too many times. Please grab yourself a snack and try again in a bit.'); + } // Find the user $user = User::where('email', $data['email'])->first(); // Compare the passwords - if (!User::checkPassHash($data['password'], $user->password)) { + if (!Hash::check($data['password'], $user->password)) { // Register the login attempt LoginAttempt::registerFailedLoginAttempt($request->ip()); @@ -47,8 +49,9 @@ public function create(CreateSessionRequest $request): JsonResponse } // Check if email is confirmed - if (!$user->hasVerifiedEmail()) + if (!$user->hasVerifiedEmail()) { throw new AuthenticationException('You have not confirmed your email address yet. Please check your email inbox or spam folder.'); + } // Create a new session $session = $user->createSession([ @@ -92,8 +95,9 @@ function update(UpdateSessionRequest $request, Session $session): JsonResponse if (count($changedFields)) { $displayMessage .= 'You have updated: ' . join(', ', $changedFields) . '.'; $session->save(); + } else { + $displayMessage .= 'No information was updated.'; } - else $displayMessage .= 'No information was updated.'; return JSONResult::success([ 'message' => $displayMessage diff --git a/app/Http/Controllers/Web/SignUpUserController.php b/app/Http/Controllers/Web/SignUpUserController.php index 4d3e098e2..e994e9852 100644 --- a/app/Http/Controllers/Web/SignUpUserController.php +++ b/app/Http/Controllers/Web/SignUpUserController.php @@ -7,6 +7,7 @@ use App\Models\User; use Auth; use Browser; +use Hash; use Illuminate\Auth\Events\Registered; use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\View\Factory; @@ -46,7 +47,7 @@ public function store(SignUpRequest $request): Application|RedirectResponse|Redi $newUser = User::create([ 'username' => $data['username'], 'email' => $data['email'], - 'password' => User::hashPass($data['password']), + 'password' => Hash::make($data['password']), 'settings' => [ 'can_change_username' => false, 'tv_rating' => -1 diff --git a/app/Mail/ResetPassword.php b/app/Mail/ResetPassword.php index c0da32b1a..ac19105fb 100644 --- a/app/Mail/ResetPassword.php +++ b/app/Mail/ResetPassword.php @@ -12,7 +12,18 @@ class ResetPassword extends Mailable { use Queueable, SerializesModels; + /** + * The user receiving the password reset email. + * + * @var User $user + */ protected User $user; + + /** + * The password reset object. + * + * @var PasswordReset $passwordReset + */ protected PasswordReset $passwordReset; /** diff --git a/tests/Traits/ProvidesTestUser.php b/tests/Traits/ProvidesTestUser.php index 7727909fd..807a2deec 100644 --- a/tests/Traits/ProvidesTestUser.php +++ b/tests/Traits/ProvidesTestUser.php @@ -3,6 +3,7 @@ namespace Tests\Traits; use App\Models\User; +use Hash; trait ProvidesTestUser { @@ -20,11 +21,11 @@ trait ProvidesTestUser protected function initializeTestUser() { $this->user = User::factory()->create([ - 'username' => 'KurozoraTester', - 'email' => 'tester@kurozora.app', + 'username' => 'KurozoraTester', + 'email' => 'tester@kurozora.app', 'email_verified_at' => now(), - 'password' => User::hashPass($this->userPassword), - 'biography' => 'Hi! This is my Kurozora account.', + 'password' => Hash::make($this->userPassword), + 'biography' => 'Hi! This is my Kurozora account.', ]); } } From 658663f623eeddbce0fcfd95db07fc9282f643ed Mon Sep 17 00:00:00 2001 From: kiritokatklian Date: Mon, 3 May 2021 00:52:22 +0200 Subject: [PATCH 3/5] [Update] Password Reset Email - Updated password reset email theme - Updated password reset expiration link to 3 hours --- app/Models/User.php | 12 + app/Notifications/ResetPassword.php | 32 ++ app/Nova/Anime.php | 2 +- config/app.php | 2 +- config/auth.php | 2 +- resources/views/components/footer.blade.php | 2 +- .../views/vendor/mail/html/header.blade.php | 7 + .../views/vendor/mail/html/message.blade.php | 27 ++ .../views/vendor/mail/html/subcopy.blade.php | 7 + .../views/vendor/mail/html/themes/default.css | 290 ++++++++++++++++++ .../views/vendor/mail/text/message.blade.php | 28 ++ 11 files changed, 407 insertions(+), 4 deletions(-) create mode 100644 app/Notifications/ResetPassword.php create mode 100644 resources/views/vendor/mail/html/header.blade.php create mode 100644 resources/views/vendor/mail/html/message.blade.php create mode 100644 resources/views/vendor/mail/html/subcopy.blade.php create mode 100644 resources/views/vendor/mail/html/themes/default.css create mode 100644 resources/views/vendor/mail/text/message.blade.php diff --git a/app/Models/User.php b/app/Models/User.php index 46ea210f5..60ffdedd0 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -16,6 +16,7 @@ use Carbon\Carbon; use Cog\Contracts\Love\Reacterable\Models\Reacterable as ReacterableContract; use Cog\Laravel\Love\Reacterable\Models\Traits\Reacterable; +use App\Notifications\ResetPassword as ResetPasswordNotification; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -604,4 +605,15 @@ function receipt(): HasOne { return $this->hasOne(UserReceipt::class); } + + /** + * Send the password reset notification. + * + * @param string $token + * @return void + */ + public function sendPasswordResetNotification($token) + { + $this->notify(new ResetPasswordNotification($token)); + } } diff --git a/app/Notifications/ResetPassword.php b/app/Notifications/ResetPassword.php new file mode 100644 index 000000000..04c9640c7 --- /dev/null +++ b/app/Notifications/ResetPassword.php @@ -0,0 +1,32 @@ +subject(Lang::get('Reset Your Kurozora ID Password')) + ->line(Lang::get('You recently made a request to reset your Kurozora ID. Please click the button below to complete the process.')) + ->line(Lang::get('This password reset link will expire in :count hours.', [ + 'count' => $expirationDuration + ])) + ->action(Lang::get('Reset Password'), $url) + ->line(Lang::get('If you did not request a password reset, it’s likely that another user has entered your email address by mistake and your account is still secure. If you believe an unauthorized person has accessed your account, you can reset your password at kurozora.app.')) + ->salutation('Kurozora Support'); + } +} diff --git a/app/Nova/Anime.php b/app/Nova/Anime.php index 8545dbc5f..98c5cd2ab 100644 --- a/app/Nova/Anime.php +++ b/app/Nova/Anime.php @@ -193,7 +193,7 @@ public function fields(Request $request): array Text::make('Copyright') ->hideFromIndex() - ->help('For example: © 2020 Kurozora B.V.'), + ->help('For example: © ' . date('Y') . ' Kurozora B.V.'), Heading::make('Flags') ->onlyOnForms(), diff --git a/config/app.php b/config/app.php index d87c29cde..064a88fe3 100644 --- a/config/app.php +++ b/config/app.php @@ -38,7 +38,7 @@ | or any other location as required by the application or its packages. */ - 'version' => '1.2.0-alpha.40', + 'version' => '1.2.0-alpha.41', /* |-------------------------------------------------------------------------- diff --git a/config/auth.php b/config/auth.php index 034c209e9..b28d20c85 100644 --- a/config/auth.php +++ b/config/auth.php @@ -96,7 +96,7 @@ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', - 'expire' => 60, + 'expire' => 180, 'throttle' => 60, ], ], diff --git a/resources/views/components/footer.blade.php b/resources/views/components/footer.blade.php index 03d9ab651..a4fc5bb21 100644 --- a/resources/views/components/footer.blade.php +++ b/resources/views/components/footer.blade.php @@ -48,7 +48,7 @@
-

Copyright ©{{ now()->year }} Kurozora B.V. {{ __('All rights reserved') }}

+

Copyright © {{ now()->year }} Kurozora B.V. {{ __('All rights reserved') }}

diff --git a/resources/views/vendor/mail/html/header.blade.php b/resources/views/vendor/mail/html/header.blade.php new file mode 100644 index 000000000..9626c85e9 --- /dev/null +++ b/resources/views/vendor/mail/html/header.blade.php @@ -0,0 +1,7 @@ + + + + {{ $slot }} + + + diff --git a/resources/views/vendor/mail/html/message.blade.php b/resources/views/vendor/mail/html/message.blade.php new file mode 100644 index 000000000..cd0cf3a0d --- /dev/null +++ b/resources/views/vendor/mail/html/message.blade.php @@ -0,0 +1,27 @@ +@component('mail::layout') + {{-- Header --}} + @slot('header') + @component('mail::header', ['url' => config('app.url')]) + {{ config('app.name') }} + @endcomponent + @endslot + + {{-- Body --}} + {{ $slot }} + + {{-- Subcopy --}} + @isset($subcopy) + @slot('subcopy') + @component('mail::subcopy') + {{ $subcopy }} + @endcomponent + @endslot + @endisset + + {{-- Footer --}} + @slot('footer') + @component('mail::footer') + Copyright © {{ date('Y') }} {{ config('app.name') }} B.V. @lang('All rights reserved.') + @endcomponent + @endslot +@endcomponent diff --git a/resources/views/vendor/mail/html/subcopy.blade.php b/resources/views/vendor/mail/html/subcopy.blade.php new file mode 100644 index 000000000..f559e8f1b --- /dev/null +++ b/resources/views/vendor/mail/html/subcopy.blade.php @@ -0,0 +1,7 @@ + + + + + diff --git a/resources/views/vendor/mail/html/themes/default.css b/resources/views/vendor/mail/html/themes/default.css new file mode 100644 index 000000000..302c4df1a --- /dev/null +++ b/resources/views/vendor/mail/html/themes/default.css @@ -0,0 +1,290 @@ +/* Base */ + +body, +body *:not(html):not(style):not(br):not(tr):not(code) { + box-sizing: border-box; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + position: relative; +} + +body { + -webkit-text-size-adjust: none; + background-color: #ffffff; + color: #718096; + height: 100%; + line-height: 1.4; + margin: 0; + padding: 0; + width: 100% !important; +} + +p, +ul, +ol, +blockquote { + line-height: 1.4; + text-align: left; +} + +a { + color: #FF9300; +} + +a img { + border: none; +} + +/* Typography */ + +h1 { + color: #3d4852; + font-size: 18px; + font-weight: bold; + margin-top: 0; + text-align: left; +} + +h2 { + font-size: 16px; + font-weight: bold; + margin-top: 0; + text-align: left; +} + +h3 { + font-size: 14px; + font-weight: bold; + margin-top: 0; + text-align: left; +} + +p { + font-size: 16px; + line-height: 1.5em; + margin-top: 0; + text-align: left; +} + +p.sub { + font-size: 12px; +} + +img { + max-width: 100%; +} + +/* Layout */ + +.wrapper { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; + background-color: #edf2f7; + margin: 0; + padding: 0; + width: 100%; +} + +.content { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; + margin: 0; + padding: 0; + width: 100%; +} + +/* Header */ + +.header { + padding: 25px 0; + text-align: center; +} + +.header a { + color: #3d4852; + font-size: 19px; + font-weight: bold; + text-decoration: none; +} + +/* Logo */ + +.logo { + height: 75px; + max-height: 75px; + width: 75px; +} + +/* Body */ + +.body { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; + background-color: #edf2f7; + border-bottom: 1px solid #edf2f7; + border-top: 1px solid #edf2f7; + margin: 0; + padding: 0; + width: 100%; +} + +.inner-body { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 570px; + background-color: #ffffff; + border-color: #e8e5ef; + border-radius: 2px; + border-width: 1px; + box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015); + margin: 0 auto; + padding: 0; + width: 570px; +} + +/* Subcopy */ + +.subcopy { + border-top: 1px solid #e8e5ef; + margin-top: 25px; + padding-top: 25px; +} + +.subcopy p { + font-size: 14px; +} + +/* Footer */ + +.footer { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 570px; + margin: 0 auto; + padding: 0; + text-align: center; + width: 570px; +} + +.footer p { + color: #b0adc5; + font-size: 12px; + text-align: center; +} + +.footer a { + color: #b0adc5; + text-decoration: underline; +} + +/* Tables */ + +.table table { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; + margin: 30px auto; + width: 100%; +} + +.table th { + border-bottom: 1px solid #edeff2; + margin: 0; + padding-bottom: 8px; +} + +.table td { + color: #74787e; + font-size: 15px; + line-height: 18px; + margin: 0; + padding: 10px 0; +} + +.content-cell { + max-width: 100vw; + padding: 32px; +} + +/* Buttons */ + +.action { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; + margin: 30px auto; + padding: 0; + text-align: center; + width: 100%; +} + +.button { + -webkit-text-size-adjust: none; + border-radius: 4px; + color: #fff; + display: inline-block; + overflow: hidden; + text-decoration: none; +} + +.button-orange, +.button-primary { + background-color: #FF9300; + border-bottom: 8px solid #FF9300; + border-left: 18px solid #FF9300; + border-right: 18px solid #FF9300; + border-top: 8px solid #FF9300; +} + +.button-green, +.button-success { + background-color: #48bb78; + border-bottom: 8px solid #48bb78; + border-left: 18px solid #48bb78; + border-right: 18px solid #48bb78; + border-top: 8px solid #48bb78; +} + +.button-red, +.button-error { + background-color: #e53e3e; + border-bottom: 8px solid #e53e3e; + border-left: 18px solid #e53e3e; + border-right: 18px solid #e53e3e; + border-top: 8px solid #e53e3e; +} + +/* Panels */ + +.panel { + border-left: #2d3748 solid 4px; + margin: 21px 0; +} + +.panel-content { + background-color: #edf2f7; + color: #718096; + padding: 16px; +} + +.panel-content p { + color: #718096; +} + +.panel-item { + padding: 0; +} + +.panel-item p:last-of-type { + margin-bottom: 0; + padding-bottom: 0; +} + +/* Utilities */ + +.break-all { + word-break: break-all; +} diff --git a/resources/views/vendor/mail/text/message.blade.php b/resources/views/vendor/mail/text/message.blade.php new file mode 100644 index 000000000..437cbdc72 --- /dev/null +++ b/resources/views/vendor/mail/text/message.blade.php @@ -0,0 +1,28 @@ +@component('mail::layout') + {{-- Header --}} + @slot('header') + @component('mail::header', ['url' => config('app.url')]) + {{ config('app.name') }} + @endcomponent + @endslot + + {{-- Body --}} + {{ $slot }} +
+ + {{-- Subcopy --}} + @isset($subcopy) + @slot('subcopy') + @component('mail::subcopy') + {{ $subcopy }} + @endcomponent + @endslot + @endisset + + {{-- Footer --}} + @slot('footer') + @component('mail::footer') + Copyright © {{ date('Y') }} {{ config('app.name') }} B.V. @lang('All rights reserved.') + @endcomponent + @endslot +@endcomponent From 22ac306a7cd692f2637965c986d3bfd3ca58ff3d Mon Sep 17 00:00:00 2001 From: kiritokatklian Date: Mon, 3 May 2021 01:03:51 +0200 Subject: [PATCH 4/5] [Remove] Old Password Reset - Removed old password reset logic in favor of the new one. --- app/Http/Controllers/UserController.php | 13 +--- .../Web/PasswordResetLinkController.php | 4 +- app/Jobs/SendNewPasswordMail.php | 60 --------------- app/Jobs/SendPasswordResetMail.php | 77 ------------------- app/Mail/ResetPassword.php | 58 -------------- app/Mail/SendNewPassword.php | 56 -------------- 6 files changed, 5 insertions(+), 263 deletions(-) delete mode 100644 app/Jobs/SendNewPasswordMail.php delete mode 100644 app/Jobs/SendPasswordResetMail.php delete mode 100644 app/Mail/ResetPassword.php delete mode 100644 app/Mail/SendNewPassword.php diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 79f995a05..0deed1ded 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -64,15 +64,10 @@ public function resetPassword(ResetPassword $request): JsonResponse { $data = $request->validated(); - // Try to find the user with this email - /** @var User $user */ - $user = User::where('email', $data['email'])->first(); - - // There is a user with this email the try to send a reset link. - // Request may be throttled if requested a lot. - if ($user) { - Password::sendResetLink(['email' => $data['email']]); - } + // We will send the password reset link to this user. Once we have attempted + // to send the link, we will examine the response then see the message we + // need to show to the user. Finally, we'll send out a proper response. + Password::sendResetLink(['email' => $data['email']]); // Show successful response return JSONResult::success(); diff --git a/app/Http/Controllers/Web/PasswordResetLinkController.php b/app/Http/Controllers/Web/PasswordResetLinkController.php index ed7b41543..3071c1bcb 100644 --- a/app/Http/Controllers/Web/PasswordResetLinkController.php +++ b/app/Http/Controllers/Web/PasswordResetLinkController.php @@ -37,9 +37,7 @@ public function store(Request $request): RedirectResponse // We will send the password reset link to this user. Once we have attempted // to send the link, we will examine the response then see the message we // need to show to the user. Finally, we'll send out a proper response. - Password::sendResetLink( - $request->only('email') - ); + Password::sendResetLink($request->only('email')); return back()->with('status', __('If an account exists with this Kurozora ID, you should receive an email with your reset link shortly.')); } diff --git a/app/Jobs/SendNewPasswordMail.php b/app/Jobs/SendNewPasswordMail.php deleted file mode 100644 index 6d954f100..000000000 --- a/app/Jobs/SendNewPasswordMail.php +++ /dev/null @@ -1,60 +0,0 @@ -user = $user; - $this->newPassword = $newPassword; - } - - /** - * Execute the job. - */ - public function handle() - { - // Send the mail - Mail::to($this->user->email) - ->send(new SendNewPassword($this->user, $this->newPassword)); - } -} diff --git a/app/Jobs/SendPasswordResetMail.php b/app/Jobs/SendPasswordResetMail.php deleted file mode 100644 index 9aaf13541..000000000 --- a/app/Jobs/SendPasswordResetMail.php +++ /dev/null @@ -1,77 +0,0 @@ -user = $user; - $this->passwordReset = $passwordReset; - } - - /** - * Execute the job. - * - * @throws Throwable - */ - public function handle() - { - $this->send(); - } - - /** - * Sends the mail - * - * @return bool - * @throws Throwable - */ - protected function send(): bool - { - // Send the mail - Mail::to($this->user->email) - ->send(new ResetPassword($this->user, $this->passwordReset)); - - return true; - } -} diff --git a/app/Mail/ResetPassword.php b/app/Mail/ResetPassword.php deleted file mode 100644 index ac19105fb..000000000 --- a/app/Mail/ResetPassword.php +++ /dev/null @@ -1,58 +0,0 @@ -user = $user; - $this->passwordReset = $passwordReset; - } - - /** - * Build the message. - * - * @return $this - */ - public function build(): static - { - return $this - ->subject('Request to reset your Kurozora password') - ->view('email.password_reset_notification') - ->with([ - 'title' => 'Password reset', - 'username' => $this->user->username, - 'ip_address' => $this->passwordReset->ip_address, - 'reset_url' => route('password.reset', ['token' => $this->passwordReset->token]) - ]); - } -} diff --git a/app/Mail/SendNewPassword.php b/app/Mail/SendNewPassword.php deleted file mode 100644 index 8c10e7e41..000000000 --- a/app/Mail/SendNewPassword.php +++ /dev/null @@ -1,56 +0,0 @@ -user = $user; - $this->newPassword = $newPassword; - } - - /** - * Build the message. - * - * @return $this - */ - public function build(): static - { - return $this - ->subject('Your new Kurozora password') - ->view('email.password_reset_new_pass') - ->with([ - 'title' => 'Your new password', - 'username' => $this->user->username, - 'newPass' => $this->newPassword - ]); - } -} From 6a871f1a44b69193af0587dfb06ab0a699c0640c Mon Sep 17 00:00:00 2001 From: kiritokatklian Date: Mon, 3 May 2021 01:06:47 +0200 Subject: [PATCH 5/5] [Update] Test - Updated reset password tests --- tests/API/ResetPasswordTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/API/ResetPasswordTest.php b/tests/API/ResetPasswordTest.php index bc6a320ca..752cf20ee 100644 --- a/tests/API/ResetPasswordTest.php +++ b/tests/API/ResetPasswordTest.php @@ -3,7 +3,7 @@ namespace Tests\API; use Exception; -use Illuminate\Auth\Notifications\ResetPassword; +use App\Notifications\ResetPassword; use Illuminate\Foundation\Testing\DatabaseMigrations; use Notification; use Tests\TestCase;