Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 138
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
AccountUserEmailLogic
0.00% covered (danger)
0.00%
0 / 138
0.00% covered (danger)
0.00%
0 / 7
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 startup
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
12
 validateImpl
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
 executeImpl
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 executeEdit
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
2
 executeConfirm
0.00% covered (danger)
0.00%
0 / 59
0.00% covered (danger)
0.00%
0 / 1
30
 cleanup
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3declare(strict_types=1);
4
5namespace PeServer\App\Models\Domain\Page\Account;
6
7use PeServer\App\Models\AppConfiguration;
8use PeServer\App\Models\AppCryptography;
9use PeServer\App\Models\AppMailer;
10use PeServer\App\Models\AppTemplate;
11use PeServer\App\Models\AuditLog;
12use PeServer\App\Models\Dao\Domain\UserDomainDao;
13use PeServer\App\Models\Dao\Entities\UserChangeWaitEmailsEntityDao;
14use PeServer\App\Models\Domain\AccountValidator;
15use PeServer\App\Models\Domain\Page\PageLogicBase;
16use PeServer\App\Models\SessionKey;
17use PeServer\Core\Cryptography;
18use PeServer\Core\Database\IDatabaseContext;
19use PeServer\Core\I18n;
20use PeServer\Core\Mail\EmailAddress;
21use PeServer\Core\Mail\EmailMessage;
22use PeServer\Core\Mail\Mailer;
23use PeServer\Core\Mvc\LogicCallMode;
24use PeServer\Core\Mvc\LogicParameter;
25use PeServer\Core\Mvc\Validator;
26use PeServer\Core\Text;
27use PeServer\Core\Throws\NotImplementedException;
28
29class AccountUserEmailLogic extends PageLogicBase
30{
31    /**
32     * Undocumented function
33     *
34     * @var array{email:string,wait_email:string,token_timestamp_utc:string}
35     */
36    private array $defaultValues = [
37        'email' => Text::EMPTY,
38        'wait_email' => Text::EMPTY,
39        'token_timestamp_utc' => Text::EMPTY,
40    ];
41
42    public function __construct(LogicParameter $parameter, private AppConfiguration $config, private AppCryptography $cryptography, private Mailer $mailer, private AppTemplate $appTemplate)
43    {
44        parent::__construct($parameter);
45    }
46
47    #region PageLogicBase
48
49    protected function startup(LogicCallMode $callMode): void
50    {
51        $userInfo = $this->requireSession(SessionKey::ACCOUNT);
52
53        $database = $this->openDatabase();
54
55        $userDomainDao = new UserDomainDao($database);
56        $values = $userDomainDao->selectEmailAndWaitTokenTimestamp(
57            $userInfo->userId,
58            $this->config->setting->config->confirm->userChangeWaitEmailMinutes
59        );
60
61        if (!Text::isNullOrWhiteSpace($values->fields['email'])) {
62            $this->defaultValues['email'] = $this->cryptography->decrypt($values->fields['email']);
63        } else {
64            $this->defaultValues['email'] = Text::EMPTY;
65        }
66        if (!Text::isNullOrWhiteSpace($values->fields['wait_email'])) {
67            $this->defaultValues['wait_email'] = $this->cryptography->decrypt($values->fields['wait_email']);
68        } else {
69            $this->defaultValues['wait_email'] = Text::EMPTY;
70        }
71
72        $this->defaultValues['token_timestamp_utc'] = $values->fields['token_timestamp_utc'];
73
74        parent::registerParameterKeys([
75            'account_email_email',
76            'account_email_token',
77            'wait_email',
78            'token_timestamp_utc',
79        ], true);
80
81        $this->setValue('account_email_token', Text::EMPTY);
82    }
83
84    protected function validateImpl(LogicCallMode $callMode): void
85    {
86        if ($callMode === LogicCallMode::Initialize) {
87            return;
88        }
89
90        $mode = $this->getRequest('account_email_mode');
91        if ($mode === 'edit') {
92            $this->validation('account_email_email', function (string $key, string $value) {
93                $accountValidator = new AccountValidator($this, $this->validator);
94                $accountValidator->isEmail($key, $value);
95            });
96        } elseif ($mode === 'confirm') {
97            $this->validation('account_email_token', function (string $key, string $value) {
98                $this->validator->isNotWhiteSpace($key, $value);
99
100                if (Text::isNullOrWhiteSpace($this->defaultValues['token_timestamp_utc'])) {
101                    $this->addError($key, I18n::message('error/email_confirm_token_not_found'));
102                }
103            });
104        } else {
105            $this->logger->warn('不明なモード要求 {0}', $mode);
106            $this->addCommonError(I18n::message('error/unknown_email_mode'));
107        }
108    }
109
110    protected function executeImpl(LogicCallMode $callMode): void
111    {
112        if ($callMode === LogicCallMode::Initialize) {
113            return;
114        }
115
116        $mode = $this->getRequest('account_email_mode');
117
118        if ($mode === 'edit') {
119            $this->executeEdit($callMode);
120        } else {
121            if ($mode !== 'confirm') {
122                throw new NotImplementedException();
123            }
124
125            $this->executeConfirm($callMode);
126        }
127    }
128
129    private function executeEdit(LogicCallMode $callMode): void
130    {
131        $account = $this->requireSession(SessionKey::ACCOUNT);
132
133        $email = $this->getRequest('account_email_email');
134
135        $params = [
136            'user_id' => $account->userId,
137            'email' => $this->cryptography->encrypt($email),
138            'mark_email' => $this->cryptography->toMark($email),
139            'token' => sprintf('%08d', Cryptography::generateRandomInteger(0, 99999999)),
140        ];
141
142        $database = $this->openDatabase();
143
144        $database->transaction(function (IDatabaseContext $context) use ($params) {
145            $userChangeWaitEmailsEntityDao = new UserChangeWaitEmailsEntityDao($context);
146
147            $userChangeWaitEmailsEntityDao->deleteByUserId($params['user_id']);
148            $userChangeWaitEmailsEntityDao->insertWaitEmails($params['user_id'], $params['email'], $params['mark_email'], $params['token']);
149
150            $this->writeAuditLogCurrentUser(AuditLog::USER_EMAIL_CHANGING, ['token' => $params['token']], $context);
151
152            return true;
153        });
154
155        // トークン通知メール送信
156        $subject = I18n::message('subject/email_change_token');
157        $values = [
158            'name' => $account->name,
159            'token' => $params['token'],
160        ];
161        $html = $this->appTemplate->createMailTemplate('change_email_token', $subject, $values);
162
163        $this->mailer->toAddresses = [
164            new EmailAddress($email, $account->name),
165        ];
166        $this->mailer->subject = $subject;
167        $this->mailer->setMessage(new EmailMessage(null, $html));
168
169        $this->mailer->send();
170        //file_put_contents('X:\00_others\00_others\a.html',$html);
171        $this->addTemporaryMessage(I18n::message('message/flash/send_email_token'));
172    }
173
174    private function executeConfirm(LogicCallMode $callMode): void
175    {
176        $account = $this->requireSession(SessionKey::ACCOUNT);
177
178        $params = [
179            'user_id' => $account->userId,
180            'token' => $this->getRequest('account_email_token'),
181        ];
182
183        $database = $this->openDatabase();
184        $result = $database->transaction(function (IDatabaseContext $context) use ($params) {
185            $userDomainDao = new UserDomainDao($context);
186            $userChangeWaitEmailsEntityDao = new UserChangeWaitEmailsEntityDao($context);
187
188            $existsToken = $userChangeWaitEmailsEntityDao->selectExistsToken(
189                $params['user_id'],
190                $params['token'],
191                $this->config->setting->config->confirm->userChangeWaitEmailMinutes
192            );
193
194            if (!$existsToken) {
195                return false;
196            }
197
198            $updated = $userDomainDao->updateEmailFromWaitEmail(
199                $params['user_id'],
200                $params['token']
201            );
202
203            if (!$updated) {
204                return false;
205            }
206
207            $userChangeWaitEmailsEntityDao->deleteByUserId($params['user_id']);
208
209            $this->writeAuditLogCurrentUser(AuditLog::USER_EMAIL_CHANGED, ['token' => $params['token']], $context);
210
211            return true;
212        });
213
214        if (!$result) {
215            $this->addError('account_email_token', I18n::message('error/email_confirm_token_not_found'));
216            return;
217        }
218
219        // 新旧メールアドレスにそれぞれ通知メール送信
220        $items = [
221            [
222                'template' => 'change_email_new',
223                'subject' => 'subject/email_change_new',
224                'email' => $this->defaultValues['wait_email'],
225            ],
226            [
227                'template' => 'change_email_old',
228                'subject' => 'subject/email_change_old',
229                'email' => $this->defaultValues['email'],
230            ],
231        ];
232
233        foreach ($items as $item) {
234            $subject = I18n::message($item['subject']);
235            $values = [
236                'user_id' => $account->userId,
237                'login_id' => $account->loginId,
238                'name' => $account->name,
239                'new_email' => $this->defaultValues['wait_email'],
240                'old_email' => $this->defaultValues['email'],
241            ];
242            $html = $this->appTemplate->createMailTemplate($item['template'], $subject, $values);
243
244            $this->mailer->toAddresses = [
245                new EmailAddress($item['email'], $account->name),
246            ];
247            $this->mailer->subject = $subject;
248            $this->mailer->setMessage(new EmailMessage(null, $html));
249
250            $this->mailer->send();
251        }
252
253        $this->result['confirm'] = true;
254
255        $this->addTemporaryMessage(I18n::message('message/flash/updated_email'));
256    }
257
258    protected function cleanup(LogicCallMode $callMode): void
259    {
260        if (!($this->getRequest('account_email_mode') == 'edit' && $callMode === LogicCallMode::Submit)) {
261            $this->setValue('account_email_email', $this->defaultValues['email']);
262        }
263
264        $this->setValue('wait_email', $this->defaultValues['wait_email']);
265        $this->setValue('token_timestamp_utc', $this->defaultValues['token_timestamp_utc']);
266    }
267
268    #endregion
269}