136 lines
4.3 KiB
PHP
136 lines
4.3 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Auth;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Http\Requests\ChangePasswordRequest;
|
||
use Illuminate\Support\Facades\Auth;
|
||
use Illuminate\Support\Facades\Hash;
|
||
use Illuminate\Auth\Events\PasswordReset;
|
||
use Illuminate\Validation\ValidationException;
|
||
use Carbon\Carbon;
|
||
|
||
class PasswordChangeController extends Controller
|
||
{
|
||
/**
|
||
* コントローラーのコンストラクタ
|
||
*
|
||
* ログイン状態のユーザーのみアクセス可能
|
||
*/
|
||
public function __construct()
|
||
{
|
||
// Laravel 12: ミドルウェアは routes/web.php で処理
|
||
}
|
||
|
||
/**
|
||
* パスワード変更フォーム表示
|
||
*
|
||
* GET /password/change
|
||
*
|
||
* @return \Illuminate\View\View
|
||
*/
|
||
public function showChangeForm()
|
||
{
|
||
// 現在のユーザー情報を取得
|
||
$ope = Auth::user();
|
||
|
||
// ビューにパスワード変更が必須かどうかを判定するデータを渡す
|
||
$isRequired = $this->isPasswordChangeRequired($ope);
|
||
|
||
return view('auth.password-change', [
|
||
'isRequired' => $isRequired,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* パスワード変更成功画面を表示
|
||
*
|
||
* GET /password/change/success
|
||
*
|
||
* @return \Illuminate\View\View
|
||
*/
|
||
public function showSuccessPage()
|
||
{
|
||
return view('auth.password-change-success');
|
||
}
|
||
|
||
/**
|
||
* パスワード変更処理
|
||
*
|
||
* POST /password/change
|
||
*
|
||
* バリデーション:
|
||
* - 当前パスワード:必填、8-64文字、ハッシュ値一致確認
|
||
* - 新パスワード:必填、8-64文字、英数字+記号のみ、当前と異なる
|
||
* - 新パスワード確認:必填、新パスワードと一致
|
||
*
|
||
* @param \App\Http\Requests\ChangePasswordRequest $request
|
||
* @return \Illuminate\Http\RedirectResponse
|
||
*/
|
||
public function updatePassword(ChangePasswordRequest $request)
|
||
{
|
||
// 現在のユーザーを取得
|
||
$ope = Auth::user();
|
||
|
||
// ステップ1:当前パスワードの認証(ハッシュ値の確認)
|
||
if (!Hash::check($request->current_password, $ope->ope_pass)) {
|
||
// バリデーションエラーとして当前パスワード が正しくないことを返す
|
||
throw ValidationException::withMessages([
|
||
'current_password' => '当前パスワードが正しくありません。',
|
||
]);
|
||
}
|
||
|
||
// ステップ2:新パスワードが当前パスワードと同一でないか確認
|
||
// FormRequest側でも not_in ルールで確認しているが、ハッシュ値での二重チェック
|
||
if (Hash::check($request->password, $ope->ope_pass)) {
|
||
throw ValidationException::withMessages([
|
||
'password' => '新パスワードは当前パスワードと異なる必要があります。',
|
||
]);
|
||
}
|
||
|
||
// ステップ3:データベース更新
|
||
// パスワードをハッシュ化して更新
|
||
$ope->ope_pass = Hash::make($request->password);
|
||
|
||
// パスワード変更時刻を現在時刻に更新
|
||
$ope->ope_pass_changed_at = Carbon::now();
|
||
|
||
// updated_at も自動更新される
|
||
$ope->save();
|
||
|
||
// イベント発火:パスワード変更イベント
|
||
event(new PasswordReset($ope));
|
||
|
||
// 成功画面へリダイレクト
|
||
return redirect()->route('password.change.success');
|
||
}
|
||
|
||
/**
|
||
* パスワード変更が必須かどうかを判定
|
||
*
|
||
* 初回ログイン時(ope_pass_changed_at が NULL)または
|
||
* 最後変更から3ヶ月以上経過している場合、TRUE を返す
|
||
*
|
||
* @param \App\Models\Ope $ope
|
||
* @return bool
|
||
*/
|
||
private function isPasswordChangeRequired($ope): bool
|
||
{
|
||
// パスワード変更日時が未設定(初回ログイン等)
|
||
if (is_null($ope->ope_pass_changed_at)) {
|
||
return true;
|
||
}
|
||
|
||
// パスワード変更から経過日数を計算
|
||
$changedAt = Carbon::parse($ope->ope_pass_changed_at);
|
||
$now = Carbon::now();
|
||
|
||
// 3ヶ月以上経過している場合
|
||
if ($now->diffInMonths($changedAt) >= 3) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
}
|