Rev 1267 | Rev 1283 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1267 | Rev 1282 | ||
---|---|---|---|
1 | <?php |
1 | <?php |
2 | 2 | ||
3 | /* |
3 | /* |
4 | * OIDplus 2.0 |
4 | * OIDplus 2.0 |
5 | * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft |
5 | * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft |
6 | * |
6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | * you may not use this file except in compliance with the License. |
8 | * you may not use this file except in compliance with the License. |
9 | * You may obtain a copy of the License at |
9 | * You may obtain a copy of the License at |
10 | * |
10 | * |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * |
12 | * |
13 | * Unless required by applicable law or agreed to in writing, software |
13 | * Unless required by applicable law or agreed to in writing, software |
14 | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * See the License for the specific language governing permissions and |
16 | * See the License for the specific language governing permissions and |
17 | * limitations under the License. |
17 | * limitations under the License. |
18 | */ |
18 | */ |
19 | 19 | ||
20 | namespace ViaThinkSoft\OIDplus; |
20 | namespace ViaThinkSoft\OIDplus; |
21 | 21 | ||
22 | // phpcs:disable PSR1.Files.SideEffects |
22 | // phpcs:disable PSR1.Files.SideEffects |
23 | \defined('INSIDE_OIDPLUS') or die; |
23 | \defined('INSIDE_OIDPLUS') or die; |
24 | // phpcs:enable PSR1.Files.SideEffects |
24 | // phpcs:enable PSR1.Files.SideEffects |
25 | 25 | ||
26 | class OIDplusPagePublicForgotPassword extends OIDplusPagePluginPublic { |
26 | class OIDplusPagePublicForgotPassword extends OIDplusPagePluginPublic { |
27 | 27 | ||
28 | /** |
28 | /** |
29 | * @param string $actionID |
29 | * @param string $actionID |
30 | * @param array $params |
30 | * @param array $params |
31 | * @return array |
31 | * @return array |
32 | * @throws OIDplusException |
32 | * @throws OIDplusException |
33 | * @throws OIDplusMailException |
33 | * @throws OIDplusMailException |
34 | */ |
34 | */ |
35 | public function action(string $actionID, array $params): array { |
35 | public function action(string $actionID, array $params): array { |
36 | if ($actionID == 'forgot_password') { |
36 | if ($actionID == 'forgot_password') { |
37 | _CheckParamExists($params, 'email'); |
37 | _CheckParamExists($params, 'email'); |
38 | $email = $params['email']; |
38 | $email = $params['email']; |
39 | 39 | ||
40 | if (!OIDplus::mailUtils()->validMailAddress($email)) { |
40 | if (!OIDplus::mailUtils()->validMailAddress($email)) { |
41 | throw new OIDplusException(_L('Invalid email address')); |
41 | throw new OIDplusException(_L('Invalid email address')); |
42 | } |
42 | } |
43 | 43 | ||
44 | OIDplus::getActiveCaptchaPlugin()->captchaVerify($params, 'captcha'); |
44 | OIDplus::getActiveCaptchaPlugin()->captchaVerify($params, 'captcha'); |
45 | 45 | ||
46 | OIDplus::logger()->log("V2:[WARN]RA(%1)", "A new password for '%1' was requested (forgot password)", $email); |
46 | OIDplus::logger()->log("V2:[WARN]RA(%1)", "A new password for '%1' was requested (forgot password)", $email); |
47 | 47 | ||
48 | $timestamp = time(); |
48 | $timestamp = time(); |
49 | $activate_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . '?goto='.urlencode('oidplus:reset_password$'.$email.'$'.$timestamp.'$'.OIDplus::authUtils()->makeAuthKey('reset_password;'.$email.';'.$timestamp)); |
49 | $activate_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . '?goto='.urlencode('oidplus:reset_password$'.$email.'$'.$timestamp.'$'.OIDplus::authUtils()->makeAuthKey('93a16dbe-f4fb-11ed-b67e-3c4a92df8582:'.$email.'/'.$timestamp)); |
50 | 50 | ||
51 | $message = $this->getForgotPasswordText($params['email']); |
51 | $message = $this->getForgotPasswordText($params['email']); |
52 | $message = str_replace('{{ACTIVATE_URL}}', $activate_url, $message); |
52 | $message = str_replace('{{ACTIVATE_URL}}', $activate_url, $message); |
53 | 53 | ||
54 | OIDplus::mailUtils()->sendMail($email, OIDplus::config()->getValue('system_title').' - Password reset request', $message); |
54 | OIDplus::mailUtils()->sendMail($email, OIDplus::config()->getValue('system_title').' - Password reset request', $message); |
55 | 55 | ||
56 | return array("status" => 0); |
56 | return array("status" => 0); |
57 | 57 | ||
58 | } else if ($actionID == 'reset_password') { |
58 | } else if ($actionID == 'reset_password') { |
59 | 59 | ||
60 | _CheckParamExists($params, 'password1'); |
60 | _CheckParamExists($params, 'password1'); |
61 | _CheckParamExists($params, 'password2'); |
61 | _CheckParamExists($params, 'password2'); |
62 | _CheckParamExists($params, 'email'); |
62 | _CheckParamExists($params, 'email'); |
63 | _CheckParamExists($params, 'auth'); |
63 | _CheckParamExists($params, 'auth'); |
64 | _CheckParamExists($params, 'timestamp'); |
64 | _CheckParamExists($params, 'timestamp'); |
65 | 65 | ||
66 | $password1 = $params['password1']; |
66 | $password1 = $params['password1']; |
67 | $password2 = $params['password2']; |
67 | $password2 = $params['password2']; |
68 | $email = $params['email']; |
68 | $email = $params['email']; |
69 | $auth = $params['auth']; |
69 | $auth = $params['auth']; |
70 | $timestamp = $params['timestamp']; |
70 | $timestamp = $params['timestamp']; |
71 | 71 | ||
72 | if (!OIDplus::authUtils()->validateAuthKey('reset_password;'.$email.';'.$timestamp, $auth)) { |
72 | if (!OIDplus::authUtils()->validateAuthKey('93a16dbe-f4fb-11ed-b67e-3c4a92df8582:'.$email.'/'.$timestamp, $auth)) { |
73 | throw new OIDplusException(_L('Invalid auth key')); |
73 | throw new OIDplusException(_L('Invalid auth key')); |
74 | } |
74 | } |
75 | 75 | ||
76 | if ((OIDplus::config()->getValue('max_ra_pwd_reset_time') > 0) && (time()-$timestamp > OIDplus::config()->getValue('max_ra_pwd_reset_time'))) { |
76 | if ((OIDplus::config()->getValue('max_ra_pwd_reset_time') > 0) && (time()-$timestamp > OIDplus::config()->getValue('max_ra_pwd_reset_time'))) { |
77 | throw new OIDplusException(_L('Invitation expired!')); |
77 | throw new OIDplusException(_L('Invitation expired!')); |
78 | } |
78 | } |
79 | 79 | ||
80 | if ($password1 !== $password2) { |
80 | if ($password1 !== $password2) { |
81 | throw new OIDplusException(_L('Passwords do not match')); |
81 | throw new OIDplusException(_L('Passwords do not match')); |
82 | } |
82 | } |
83 | 83 | ||
84 | if (strlen($password1) < OIDplus::config()->getValue('ra_min_password_length')) { |
84 | if (strlen($password1) < OIDplus::config()->getValue('ra_min_password_length')) { |
85 | $minlen = OIDplus::config()->getValue('ra_min_password_length'); |
85 | $minlen = OIDplus::config()->getValue('ra_min_password_length'); |
86 | throw new OIDplusException(_L('Password is too short. Need at least %1 characters',$minlen)); |
86 | throw new OIDplusException(_L('Password is too short. Need at least %1 characters',$minlen)); |
87 | } |
87 | } |
88 | 88 | ||
89 | OIDplus::logger()->log("V2:[INFO]RA(%1)", "RA '%1' has reset his password (forgot passwort)", $email); |
89 | OIDplus::logger()->log("V2:[INFO]RA(%1)", "RA '%1' has reset his password (forgot passwort)", $email); |
90 | 90 | ||
91 | $ra = new OIDplusRA($email); |
91 | $ra = new OIDplusRA($email); |
92 | $ra->change_password($password1); |
92 | $ra->change_password($password1); |
93 | 93 | ||
94 | return array("status" => 0); |
94 | return array("status" => 0); |
95 | } else { |
95 | } else { |
96 | return parent::action($actionID, $params); |
96 | return parent::action($actionID, $params); |
97 | } |
97 | } |
98 | } |
98 | } |
99 | 99 | ||
100 | /** |
100 | /** |
101 | * @param bool $html |
101 | * @param bool $html |
102 | * @return void |
102 | * @return void |
103 | * @throws OIDplusException |
103 | * @throws OIDplusException |
104 | */ |
104 | */ |
105 | public function init(bool $html=true) { |
105 | public function init(bool $html=true) { |
106 | OIDplus::config()->prepareConfigKey('max_ra_pwd_reset_time', 'Max RA password reset time in seconds (0 = infinite)', '0', OIDplusConfig::PROTECTION_EDITABLE, function($value) { |
106 | OIDplus::config()->prepareConfigKey('max_ra_pwd_reset_time', 'Max RA password reset time in seconds (0 = infinite)', '0', OIDplusConfig::PROTECTION_EDITABLE, function($value) { |
107 | if (!is_numeric($value) || ($value < 0)) { |
107 | if (!is_numeric($value) || ($value < 0)) { |
108 | throw new OIDplusException(_L('Please enter a valid value.')); |
108 | throw new OIDplusException(_L('Please enter a valid value.')); |
109 | } |
109 | } |
110 | }); |
110 | }); |
111 | } |
111 | } |
112 | 112 | ||
113 | /** |
113 | /** |
114 | * @param string $id |
114 | * @param string $id |
115 | * @param array $out |
115 | * @param array $out |
116 | * @param bool $handled |
116 | * @param bool $handled |
117 | * @return void |
117 | * @return void |
118 | * @throws OIDplusException |
118 | * @throws OIDplusException |
119 | */ |
119 | */ |
120 | public function gui(string $id, array &$out, bool &$handled) { |
120 | public function gui(string $id, array &$out, bool &$handled) { |
121 | if (explode('$',$id)[0] == 'oidplus:forgot_password') { |
121 | if (explode('$',$id)[0] == 'oidplus:forgot_password') { |
122 | $handled = true; |
122 | $handled = true; |
123 | 123 | ||
124 | $out['title'] = _L('Forgot password'); |
124 | $out['title'] = _L('Forgot password'); |
125 | $out['icon'] = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/forgot_password_icon.png'; |
125 | $out['icon'] = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/forgot_password_icon.png'; |
126 | 126 | ||
127 | try { |
127 | try { |
128 | $out['text'] .= '<p>'._L('Please enter the email address of your account, and information about the password reset will be sent to you.').'</p> |
128 | $out['text'] .= '<p>'._L('Please enter the email address of your account, and information about the password reset will be sent to you.').'</p> |
129 | <form id="forgotPasswordForm" action="javascript:void(0);" onsubmit="return OIDplusPagePublicForgotPassword.forgotPasswordFormOnSubmit();"> |
129 | <form id="forgotPasswordForm" action="javascript:void(0);" onsubmit="return OIDplusPagePublicForgotPassword.forgotPasswordFormOnSubmit();"> |
130 | '._L('E-Mail').': <input type="text" id="email" value=""/><br><br> |
130 | '._L('E-Mail').': <input type="text" id="email" value=""/><br><br> |
131 | '.OIDplus::getActiveCaptchaPlugin()->captchaGenerate().' |
131 | '.OIDplus::getActiveCaptchaPlugin()->captchaGenerate().' |
132 | <br> |
132 | <br> |
133 | <input type="submit" value="'._L('Send recovery information').'"> |
133 | <input type="submit" value="'._L('Send recovery information').'"> |
134 | </form>'; |
134 | </form>'; |
135 | 135 | ||
136 | } catch (\Exception $e) { |
136 | } catch (\Exception $e) { |
137 | 137 | ||
138 | $htmlmsg = $e instanceof OIDplusException ? $e->getHtmlMessage() : htmlentities($e->getMessage()); |
138 | $htmlmsg = $e instanceof OIDplusException ? $e->getHtmlMessage() : htmlentities($e->getMessage()); |
139 | throw new OIDplusHtmlException(_L('Error: %1',$htmlmsg), $out['title']); |
139 | throw new OIDplusHtmlException(_L('Error: %1',$htmlmsg), $out['title']); |
140 | 140 | ||
141 | } |
141 | } |
142 | } else if (explode('$',$id)[0] == 'oidplus:reset_password') { |
142 | } else if (explode('$',$id)[0] == 'oidplus:reset_password') { |
143 | $handled = true; |
143 | $handled = true; |
144 | 144 | ||
145 | $email = explode('$',$id)[1]; |
145 | $email = explode('$',$id)[1]; |
146 | $timestamp = explode('$',$id)[2]; |
146 | $timestamp = explode('$',$id)[2]; |
147 | $auth = explode('$',$id)[3]; |
147 | $auth = explode('$',$id)[3]; |
148 | 148 | ||
149 | $out['title'] = _L('Reset password'); |
149 | $out['title'] = _L('Reset password'); |
150 | $out['icon'] = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/reset_password_icon.png'; |
150 | $out['icon'] = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/reset_password_icon.png'; |
151 | 151 | ||
152 | if (!OIDplus::authUtils()->validateAuthKey('reset_password;'.$email.';'.$timestamp, $auth)) { |
152 | if (!OIDplus::authUtils()->validateAuthKey('reset_password;'.$email.';'.$timestamp, $auth)) { |
153 | throw new OIDplusException(_L('Invalid authorization. Is the URL OK?'), $out['title']); |
153 | throw new OIDplusException(_L('Invalid authorization. Is the URL OK?'), $out['title']); |
154 | } else { |
154 | } else { |
155 | $out['text'] = '<p>'._L('E-Mail-Address: %1','<b>'.$email.'</b>').'</p> |
155 | $out['text'] = '<p>'._L('E-Mail-Address: %1','<b>'.$email.'</b>').'</p> |
156 | 156 | ||
157 | <form id="resetPasswordForm" action="javascript:void(0);" onsubmit="return OIDplusPagePublicForgotPassword.resetPasswordFormOnSubmit();"> |
157 | <form id="resetPasswordForm" action="javascript:void(0);" onsubmit="return OIDplusPagePublicForgotPassword.resetPasswordFormOnSubmit();"> |
158 | <input type="hidden" id="email" value="'.htmlentities($email).'"/> |
158 | <input type="hidden" id="email" value="'.htmlentities($email).'"/> |
159 | <input type="hidden" id="timestamp" value="'.htmlentities($timestamp).'"/> |
159 | <input type="hidden" id="timestamp" value="'.htmlentities($timestamp).'"/> |
160 | <input type="hidden" id="auth" value="'.htmlentities($auth).'"/> |
160 | <input type="hidden" id="auth" value="'.htmlentities($auth).'"/> |
161 | <div><label class="padding_label">'._L('New password').':</label><input type="password" id="password1" value=""/></div> |
161 | <div><label class="padding_label">'._L('New password').':</label><input type="password" id="password1" value=""/></div> |
162 | <div><label class="padding_label">'._L('Repeat').':</label><input type="password" id="password2" value=""/></div> |
162 | <div><label class="padding_label">'._L('Repeat').':</label><input type="password" id="password2" value=""/></div> |
163 | <br><input type="submit" value="'._L('Change password').'"> |
163 | <br><input type="submit" value="'._L('Change password').'"> |
164 | </form>'; |
164 | </form>'; |
165 | } |
165 | } |
166 | } |
166 | } |
167 | } |
167 | } |
168 | 168 | ||
169 | /** |
169 | /** |
170 | * @param array $out |
170 | * @param array $out |
171 | * @return void |
171 | * @return void |
172 | */ |
172 | */ |
173 | public function publicSitemap(array &$out) { |
173 | public function publicSitemap(array &$out) { |
174 | $out[] = 'oidplus:forgot_password'; |
174 | $out[] = 'oidplus:forgot_password'; |
175 | } |
175 | } |
176 | 176 | ||
177 | /** |
177 | /** |
178 | * @param array $json |
178 | * @param array $json |
179 | * @param string|null $ra_email |
179 | * @param string|null $ra_email |
180 | * @param bool $nonjs |
180 | * @param bool $nonjs |
181 | * @param string $req_goto |
181 | * @param string $req_goto |
182 | * @return bool |
182 | * @return bool |
183 | */ |
183 | */ |
184 | public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool { |
184 | public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool { |
185 | return false; |
185 | return false; |
186 | } |
186 | } |
187 | 187 | ||
188 | /** |
188 | /** |
189 | * @param string $email |
189 | * @param string $email |
190 | * @return string |
190 | * @return string |
191 | * @throws OIDplusException |
191 | * @throws OIDplusException |
192 | */ |
192 | */ |
193 | private function getForgotPasswordText(string $email): string { |
193 | private function getForgotPasswordText(string $email): string { |
194 | $res = OIDplus::db()->query("select * from ###ra where email = ?", array($email)); |
194 | $res = OIDplus::db()->query("select * from ###ra where email = ?", array($email)); |
195 | if (!$res->any()) { |
195 | if (!$res->any()) { |
196 | throw new OIDplusException(_L('This RA does not exist.')); |
196 | throw new OIDplusException(_L('This RA does not exist.')); |
197 | } |
197 | } |
198 | 198 | ||
199 | $message = file_get_contents(__DIR__ . '/forgot_password.tpl'); |
199 | $message = file_get_contents(__DIR__ . '/forgot_password.tpl'); |
200 | 200 | ||
201 | // Resolve stuff |
201 | // Resolve stuff |
202 | // Note: {{ACTIVATE_URL}} will be resolved in ajax.php |
202 | // Note: {{ACTIVATE_URL}} will be resolved in ajax.php |
203 | 203 | ||
204 | $message = str_replace('{{SYSTEM_URL}}', OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL), $message); |
204 | $message = str_replace('{{SYSTEM_URL}}', OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL), $message); |
205 | 205 | ||
206 | return str_replace('{{ADMIN_EMAIL}}', OIDplus::config()->getValue('admin_email'), $message); |
206 | return str_replace('{{ADMIN_EMAIL}}', OIDplus::config()->getValue('admin_email'), $message); |
207 | } |
207 | } |
208 | 208 | ||
209 | /** |
209 | /** |
210 | * @param string $request |
210 | * @param string $request |
211 | * @return array|false |
211 | * @return array|false |
212 | */ |
212 | */ |
213 | public function tree_search(string $request) { |
213 | public function tree_search(string $request) { |
214 | return false; |
214 | return false; |
215 | } |
215 | } |
216 | } |
216 | } |
217 | 217 |