Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.00% |
48 / 64 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
AccountLoginLogic | |
75.00% |
48 / 64 |
|
75.00% |
3 / 4 |
17.06 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
startup | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
validateImpl | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
4 | |||
executeImpl | |
68.00% |
34 / 50 |
|
0.00% |
0 / 1 |
10.10 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\App\Models\Domain\Page\Account; |
6 | |
7 | use PeServer\App\Models\AuditLog; |
8 | use PeServer\App\Models\Dao\Domain\UserDomainDao; |
9 | use PeServer\App\Models\Dao\Entities\UserAuthenticationsEntityDao; |
10 | use PeServer\App\Models\Dao\Entities\UsersEntityDao; |
11 | use PeServer\App\Models\Data\SessionAccount; |
12 | use PeServer\App\Models\Data\SessionAnonymous; |
13 | use PeServer\App\Models\Domain\Page\PageLogicBase; |
14 | use PeServer\App\Models\Domain\Page\SessionAnonymousTrait; |
15 | use PeServer\App\Models\Domain\UserLevel; |
16 | use PeServer\App\Models\SessionKey; |
17 | use PeServer\Core\Cryptography; |
18 | use PeServer\Core\Http\HttpStatus; |
19 | use PeServer\Core\I18n; |
20 | use PeServer\Core\Mvc\LogicCallMode; |
21 | use PeServer\Core\Mvc\LogicParameter; |
22 | use PeServer\Core\Mvc\Validator; |
23 | use PeServer\Core\Web\WebSecurity; |
24 | use PeServer\Core\Text; |
25 | |
26 | class AccountLoginLogic extends PageLogicBase |
27 | { |
28 | use SessionAnonymousTrait; |
29 | |
30 | #region define |
31 | |
32 | private const ERROR_LOGIN_PARAMETER = 'error/login_parameter'; |
33 | |
34 | #endregion |
35 | |
36 | public function __construct(LogicParameter $parameter) |
37 | { |
38 | parent::__construct($parameter); |
39 | } |
40 | |
41 | #region PageLogicBase |
42 | |
43 | protected function startup(LogicCallMode $callMode): void |
44 | { |
45 | $this->registerParameterKeys([ |
46 | 'account_login_login_id', |
47 | 'account_login_password', |
48 | ], false); |
49 | } |
50 | |
51 | protected function validateImpl(LogicCallMode $callMode): void |
52 | { |
53 | if ($callMode === LogicCallMode::Initialize) { |
54 | return; |
55 | } |
56 | |
57 | $this->throwHttpStatusIfNotLogin(HttpStatus::NotFound); |
58 | |
59 | $loginId = $this->getRequest('account_login_login_id'); |
60 | if (Text::isNullOrWhiteSpace($loginId)) { |
61 | $this->addCommonError(I18n::message(self::ERROR_LOGIN_PARAMETER)); |
62 | } |
63 | |
64 | $password = $this->getRequest('account_login_password'); |
65 | if (Text::isNullOrWhiteSpace($password)) { |
66 | $this->addCommonError(I18n::message(self::ERROR_LOGIN_PARAMETER)); |
67 | } |
68 | } |
69 | |
70 | protected function executeImpl(LogicCallMode $callMode): void |
71 | { |
72 | if ($callMode === LogicCallMode::Initialize) { |
73 | $this->setSession(SessionKey::ANONYMOUS, new SessionAnonymous(login: true)); |
74 | return; |
75 | } |
76 | |
77 | $database = $this->openDatabase(); |
78 | |
79 | $usersEntityDao = new UsersEntityDao($database); |
80 | $userAuthenticationsEntityDao = new UserAuthenticationsEntityDao($database); |
81 | $userDomainDao = new UserDomainDao($database); |
82 | |
83 | $existsSetupUser = $usersEntityDao->selectExistsSetupUser(); |
84 | if ($existsSetupUser) { |
85 | $this->logger->info('セットアップ ユーザー 検証'); |
86 | } else { |
87 | $this->logger->info('通常 ユーザー 検証'); |
88 | } |
89 | |
90 | $user = $userDomainDao->selectLoginUser($this->getRequest('account_login_login_id')); |
91 | |
92 | if ($user === null) { |
93 | $this->addCommonError(I18n::message(self::ERROR_LOGIN_PARAMETER)); |
94 | return; |
95 | } |
96 | |
97 | if ($existsSetupUser && $user->level !== UserLevel::SETUP) { |
98 | $this->addCommonError(I18n::message(self::ERROR_LOGIN_PARAMETER)); |
99 | $this->logger->error('未セットアップ状態での通常ログインは抑制中'); |
100 | return; |
101 | } |
102 | |
103 | $loginRawPassword = $this->getRequest('account_login_password'); |
104 | // パスワード突合 |
105 | $verifyOk = Cryptography::verifyPassword($loginRawPassword, $user->currentPassword); |
106 | if (!$verifyOk) { |
107 | $this->addCommonError(I18n::message(self::ERROR_LOGIN_PARAMETER)); |
108 | $this->logger->warn('ログイン失敗: {0}', $user->userId); |
109 | $this->writeAuditLogTargetUser($user->userId, AuditLog::LOGIN_FAILED); |
110 | return; |
111 | } |
112 | // パスワードのアルゴリズムが古い場合に再設定する(業務ロジックのポリシー云々ではない) |
113 | $isNeedRehashPassword = Cryptography::needsRehashPassword($user->currentPassword); |
114 | if ($isNeedRehashPassword) { |
115 | $info = Cryptography::getPasswordInformation($user->currentPassword); |
116 | $this->logger->info("[OLD] password needs rehash: {0}, {1}", $user->userId, $info); |
117 | $loginNewPassword = Cryptography::hashPassword($loginRawPassword); |
118 | $database->transaction(function ($context) use ($user, $loginNewPassword) { |
119 | $userAuthenticationsEntityDao = new UserAuthenticationsEntityDao($context); |
120 | $userAuthenticationsEntityDao->updatePasswordOnly($user->userId, $loginNewPassword); |
121 | $info = Cryptography::getPasswordInformation($loginNewPassword); |
122 | $this->logger->info("[NEW] password needs rehash: {0}, {1}", $user->userId, $info); |
123 | return true; |
124 | }); |
125 | } |
126 | |
127 | $this->removeSession(self::SESSION_ALL_CLEAR); |
128 | $account = new SessionAccount( |
129 | $user->userId, |
130 | $user->loginId, |
131 | $user->name, |
132 | $user->level, |
133 | $user->state |
134 | ); |
135 | $this->setSession(SessionKey::ACCOUNT, $account); |
136 | $this->restartSession(); |
137 | $this->writeAuditLogCurrentUser(AuditLog::LOGIN_SUCCESS, $account); |
138 | |
139 | $userAuthenticationsEntityDao->updateClearReminder($account->userId); |
140 | } |
141 | |
142 | #endregion |
143 | } |