the browser-facing portion of osu!
at master 4.5 kB view raw
1<?php 2 3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4// See the LICENCE file in the repository root for full licence text. 5 6declare(strict_types=1); 7 8namespace App\Http\Controllers; 9 10use App\Exceptions\User\PasswordResetFailException; 11use App\Libraries\User\PasswordResetData; 12use App\Models\User; 13use App\Models\UserAccountHistory; 14use Carbon\CarbonImmutable; 15 16class PasswordResetController extends Controller 17{ 18 private static function getUser(?string $username): ?User 19 { 20 return present($username) ? User::findForLogin($username, true) : null; 21 } 22 23 public function __construct() 24 { 25 parent::__construct(); 26 27 $this->middleware('guest'); 28 $this->middleware('throttle:60,10'); 29 $this->middleware('throttle:20,1440,password-reset:'); 30 } 31 32 public function create() 33 { 34 $username = get_string(\Request::input('username')); 35 $user = static::getUser($username); 36 $error = PasswordResetData::create($user, $username); 37 38 if ($error === null) { 39 \Session::flash('popup', osu_trans('password_reset.notice.sent')); 40 41 return ujs_redirect(route('password-reset.reset', [ 42 'username' => $username, 43 ])); 44 } else { 45 return response(['form_error' => [ 46 'username' => [$error], 47 ]], 422); 48 } 49 } 50 51 public function index() 52 { 53 return ext_view('password_reset.index'); 54 } 55 56 public function resendMail() 57 { 58 $username = get_string(\Request::input('username')); 59 $user = static::getUser($username) ?? abort(422); 60 $data = PasswordResetData::find($user, $username); 61 62 if ($data === null) { 63 \Session::flash('popup', osu_trans('password_reset.error.expired')); 64 65 return ujs_redirect(route('password-reset')); 66 } elseif ($data->sendMail()) { 67 $data->save(); 68 } 69 70 return ['message' => osu_trans('password_reset.notice.sent')]; 71 } 72 73 public function reset() 74 { 75 $username = presence(get_string(\Request::input('username'))) ?? abort(422); 76 77 return ext_view('password_reset.reset', compact('username')); 78 } 79 80 public function update() 81 { 82 $params = get_params(\Request::all(), null, [ 83 'key', 84 'user.password', 85 'user.password_confirmation', 86 'username', 87 ], ['null_missing' => true]); 88 89 try { 90 $user = static::getUser($params['username']) 91 ?? throw new PasswordResetFailException('invalid'); 92 $data = PasswordResetData::find($user, $params['username']) 93 ?? throw new PasswordResetFailException('invalid'); 94 95 if (!$data->isActive()) { 96 throw new PasswordResetFailException('expired'); 97 } 98 99 $params['key'] = strtr($params['key'] ?? '', [' ' => '']); 100 if (!present($params['key'])) { 101 return response(['form_error' => [ 102 'key' => [osu_trans('password_reset.error.missing_key')], 103 ]], 422); 104 } 105 106 if (!$data->isValidKey($params['key'])) { 107 if (!$data->hasMoreTries()) { 108 throw new PasswordResetFailException('too_many_tries'); 109 } 110 111 $data->save(); 112 113 return response(['form_error' => [ 114 'key' => [osu_trans('password_reset.error.wrong_key')], 115 ]], 422); 116 } 117 } catch (PasswordResetFailException $e) { 118 if (isset($data)) { 119 $data->delete(); 120 } 121 122 \Session::flash('popup', osu_trans("password_reset.error.{$e->getMessage()}")); 123 124 return ujs_redirect(route('password-reset')); 125 } 126 127 $user->validatePasswordConfirmation(); 128 $params['user']['user_lastvisit'] = CarbonImmutable::now(); 129 if ($user->update($params['user'])) { 130 $user->resetSessions(); 131 $this->login($user); 132 133 UserAccountHistory::logUserResetPassword($user); 134 $data->delete(); 135 136 \Session::flash('popup', osu_trans('password_reset.notice.saved')); 137 138 return ujs_redirect(route('home')); 139 } else { 140 return response(['form_error' => [ 141 'user' => $user->validationErrors()->all(), 142 ]], 422); 143 } 144 } 145}