1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15:
16: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
17:
18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
30: class cPasswordRequest {
31:
32: 33: 34: 35: 36:
37: protected $_db;
38:
39: 40: 41: 42: 43:
44: protected $_cfg;
45:
46: 47: 48: 49: 50:
51: protected $_tpl;
52:
53: 54: 55: 56: 57:
58: protected $_username;
59:
60: 61: 62: 63: 64:
65: protected $_email;
66:
67: 68: 69: 70: 71:
72: protected $_reloadTime;
73:
74: 75: 76: 77: 78:
79: protected $_tokenLength;
80:
81: 82: 83: 84: 85: 86:
87: protected $_isEnabled;
88:
89: 90: 91: 92: 93:
94: protected $_sendermail;
95:
96: 97: 98: 99: 100:
101: protected $_sendername;
102:
103: 104: 105: 106: 107: 108:
109: public function __construct($db, $cfg) {
110:
111: if (!is_object($db)) {
112: $this->_db = cRegistry::getDb();
113: } else {
114: $this->_db = $db;
115: }
116:
117:
118: $this->_cfg = $cfg;
119: $this->_tpl = new cTemplate();
120: $this->_username = '';
121: $this->_email = '';
122:
123:
124: $this->_reloadTime = 240;
125:
126:
127: $this->_tokenLength = 14;
128:
129:
130:
131: $sEnable = getSystemProperty('pw_request', 'enable');
132: if ($sEnable == 'false') {
133: $this->_isEnabled = false;
134: } else {
135: $this->_isEnabled = true;
136: }
137:
138:
139:
140: $sendermail = getSystemProperty('system', 'mail_sender');
141: if (preg_match("/^.+@.+\.([A-Za-z0-9\-_]{1,20})$/", $sendermail)) {
142: $this->_sendermail = $sendermail;
143: } else {
144: $this->_sendermail = 'info@contenido.org';
145: }
146:
147:
148: $sendername = getSystemProperty('system', 'mail_sender_name');
149: if ($sendername != '') {
150: $this->_sendername = $sendername;
151: } else {
152: $this->_sendername = 'CONTENIDO Backend';
153: }
154:
155:
156:
157: if (true === $this->_isEnabled) {
158:
159: if (isset($_GET['pw_reset'])
160: && '' !== $_GET['pw_reset']) {
161:
162: $aRequests = $this->_getCurrentRequests();
163: if (count($aRequests) > 0) {
164:
165: if (false === isset($_POST['user_name'])
166: || false === isset($_POST['user_pw'])
167: || false === isset($_POST['user_pw_repeat'])) {
168:
169: $this->renderNewPwForm();
170: } else {
171:
172: $this->_handleResetPw();
173: }
174: }
175: }
176: }
177: }
178:
179: 180: 181: 182: 183: 184: 185: 186:
187: public function renderForm($return = false) {
188:
189: if (!$this->_isEnabled) {
190: return '';
191: }
192:
193: $message = '';
194:
195:
196:
197: if (isset($_POST['action']) && $_POST['action'] == 'request_pw') {
198:
199:
200: $this->_username = $_POST['request_username'];
201:
202: $message = $this->_handleNewPassword();
203:
204: $this->_tpl->set('s', 'JS_CALL', 'showRequestLayer();');
205: } else {
206:
207: $this->_tpl->set('s', 'JS_CALL', '');
208: }
209:
210:
211: $form = new cHTMLForm('request_pw', 'index.php', 'post');
212:
213:
214: $inputUsername = new cHTMLTextbox('request_username', stripslashes($_POST['request_username']), '', '', 'request_username');
215: $inputUsername->setStyle('width:215px;');
216:
217:
218: $form->setVar('action', 'request_pw');
219: $form->setVar('belang', $GLOBALS['belang']);
220:
221:
222: $form->setContent('<input class="password_request_input" type="image" src="images/submit.gif" alt="' . i18n('Submit') . '" title="' . i18n('Submit') . '">' . $inputUsername->render());
223: $this->_tpl->set('s', 'FORM', $form->render());
224: $this->_tpl->set('s', 'MESSAGE', $message);
225: $this->_tpl->set('s', 'LABEL', i18n('Please enter your login') . ':');
226:
227:
228: return $this->_tpl->generate($this->_cfg['path']['contenido'] . $this->_cfg['path']['templates'] . $this->_cfg['templates']['request_password'], $return);
229: }
230:
231: 232: 233:
234: public function renderNewPwForm() {
235: if (isset($_POST['action']) && $_POST['action'] == 'reset_pw') {
236: $this->_username = $_POST['request_username'];
237:
238: $message = $this->_handleNewPassword();
239:
240: $this->_tpl->set('s', 'JS_CALL', '');
241: } else {
242:
243: $this->_tpl->set('s', 'JS_CALL', 'showResetLayer();');
244: }
245:
246: $msg = i18n('You may now set a new password');
247: $this->_tpl->set('s', 'RESET_LABEL', $msg);
248:
249:
250: $form = new cHTMLForm('reset_form', htmlentities(cRegistry::getBackendUrl()) . '?pw_reset=' . $_GET['pw_reset']);
251:
252: $fields = array();
253: $userNameLbl = new cHTMLDiv(new cHTMLLabel(i18n('User name') . ': ', 'user_name'));
254: $userNameBox = new cHTMLTextbox('user_name');
255: $userNameBox->removeAttribute('size');
256: $userNameBox = new cHTMLDiv($userNameBox);
257:
258: $userPwLbl = new cHTMLLabel(i18n('New password') . ': ', 'user_pw');
259: $userPwBox = new cHTMLTextbox('user_pw');
260: $userPwBox->setAttribute('type', 'password');
261: $userPwBox->removeAttribute('size');
262: $userPwBox = new cHTMLDiv($userPwBox);
263:
264: $userPwRepeatLbl = new cHTMLLabel(i18n('Confirm new password'), 'user_pw_repeat');
265: $userPwRepeatBox = new cHTMLTextbox('user_pw_repeat');
266: $userPwRepeatBox->setAttribute('type', 'password');
267: $userPwRepeatBox->removeAttribute('size');
268:
269: $sendBtn = new cHTMLButton('submit');
270: $sendBtn->setAttribute('type', 'image');
271: $sendBtn->setAttribute('src', 'images/submit.gif');
272: $sendBtn->setAttribute('alt', i18n('Submit'));
273: $sendBtn->setAttribute('title', i18n('Submit'));
274:
275: $sendBtn->removeAttribute('value');
276: $form->setContent(array($userNameLbl, $userNameBox, $userPwLbl, $userPwBox, $userPwRepeatLbl, $userPwRepeatBox, $sendBtn));
277:
278: $this->_tpl->set('s', 'RESET_MESSAGE', '');
279:
280: $this->_tpl->set('s', 'RESET_FORM', $form->render());
281: }
282:
283: 284: 285:
286: protected function _getCurrentRequests() {
287: $oApiUserPasswordRequest = new cApiUserPasswordRequestCollection();
288:
289: return $oApiUserPasswordRequest->fetchCurrentRequests();
290: }
291:
292: 293: 294: 295: 296: 297:
298: protected function _handleNewPassword() {
299:
300: $message = '';
301:
302:
303:
304:
305: $sql = "SELECT username, email FROM " . $this->_cfg['tab']['user'] . "
306: WHERE username = '" . $this->_db->escape($this->_username) . "'
307: AND (valid_from <= NOW() OR valid_from = '0000-00-00 00:00:00' OR valid_from IS NULL)
308: AND (valid_to >= NOW() OR valid_to = '0000-00-00 00:00:00' OR valid_to IS NULL)";
309:
310: $this->_db->query($sql);
311: if ($this->_db->nextRecord() && md5($this->_username) == md5($this->_db->f('username'))) {
312:
313: $isAllowed = true;
314:
315:
316: $lastPwRequest = '0000-00-00 00:00:00';
317:
318:
319: $oApiUser = new cApiUser();
320:
321:
322: if (false === $oApiUser->loadBy('username', $this->_username)) {
323: $isAllowed = false;
324: $message = i18n('New password was submitted to your e-mail address.');
325: } else {
326: $oApiPasswordRequestCol = new cApiUserPasswordRequestCollection();
327: $requests = $oApiPasswordRequestCol->fetchAvailableRequests();
328:
329:
330: foreach ($requests as $oApiUserPasswordRequest) {
331:
332: $reqTime = $oApiUserPasswordRequest->get('request');
333:
334:
335: if (strtotime($lastPwRequest) < strtotime($reqTime)
336: && $this->_db->f($oApiUser->primaryKey) === $oApiUser->get($oApiUser->primaryKey)) {
337: $lastPwRequest = $reqTime;
338: }
339:
340:
341:
342: if (false === ($outdatedStr = getEffectiveSetting('pw_request', 'outdated_threshold', false))
343: || '' === $outdatedStr) {
344: $outdatedStr = '-1 day';
345: }
346:
347:
348: $outdated = new DateTime('now', new DateTimeZone('UTC'));
349: $outdated->modify($outdatedStr);
350: $expiration = new DateTime($oApiUserPasswordRequest->get('expiration'), new DateTimeZone('UTC'));
351: if (false === $oApiUserPasswordRequest->get('expiration')
352: || '' === $oApiUserPasswordRequest->get('expiration')
353: || $expiration < $outdated) {
354:
355: $oApiPasswordRequestCol->delete($oApiUserPasswordRequest->get($oApiUserPasswordRequest->primaryKey));
356: }
357: }
358:
359:
360: $uid = $oApiUser->get($oApiUser->primaryKey);
361: $requests = $oApiPasswordRequestCol->fetchAvailableRequests($uid);
362:
363:
364: if (false === ($resetThreshold = getEffectiveSetting('pw_request', 'reset_threshold', false))
365: || '' === $resetThreshold) {
366:
367: $resetThreshold = 4;
368: }
369:
370:
371: if (count($requests) > $resetThreshold) {
372: $isAllowed = false;
373: $message = i18n('Too many password reset requests. You may wait before requesting a new password.');
374: }
375: unset($requests);
376: }
377:
378:
379: $this->_email = $this->_db->f('email');
380:
381:
382: if (preg_match('/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/', $lastPwRequest, $aMatches)) {
383: $lastRequest = mktime($aMatches[4], $aMatches[5], $aMatches[6], $aMatches[2], $aMatches[3], $aMatches[1]);
384:
385:
386: if ((time() - $lastRequest) < (60 * $this->_reloadTime)) {
387:
388: $isAllowed = false;
389: $message = sprintf(i18n('Password requests are allowed every %s minutes.'), $this->_reloadTime);
390: }
391: }
392:
393: $this->_username = stripslashes($this->_username);
394:
395:
396:
397:
398:
399: if ((!preg_match("/^.+@.+\.([A-Za-z0-9\-_]{1,20})$/", $this->_email) || $this->_email == 'sysadmin@IhreSite.de' || $this->_email == 'admin_kunde@IhreSite.de') && $isAllowed) {
400: $isAllowed = false;
401:
402:
403:
404: $message = i18n('No matching data found. Please contact your system administrator.');
405: }
406:
407:
408:
409: if ($isAllowed) {
410:
411: $token = $this->_generateToken();
412:
413:
414:
415: $expiration = new DateTime('+4 hour', new DateTimeZone('UTC'));
416:
417: if (false !== $token
418: && false !== $this->_safePwResetRequest($token, $expiration)) {
419: $this->_submitMail($token);
420: $message = i18n('New password was submitted to your e-mail address.');
421: } else {
422: $message = i18n('An unknown problem occurred. Please contact your system administrator.');
423: }
424: } else {
425: sleep(5);
426: }
427: } else {
428:
429:
430: $message = i18n('No matching data found. Please contact your system administrator.');
431: sleep(5);
432: }
433: return $message;
434: }
435:
436: 437: 438:
439: protected function _handleResetPw() {
440: $this->_tpl->set('s', 'JS_CALL', 'showResetLayer();');
441: $username = (string) $_POST['user_name'];
442: $pw = (string) $_POST['user_pw'];
443: $pwRepeat = (string) $_POST['user_pw_repeat'];
444:
445: if (0 === strlen($username)) {
446: $this->_tpl->set('s', 'RESET_MESSAGE', i18n('Username can\'t be empty'));
447: $this->_tpl->set('s', 'RESET_LABEL', '');
448: $this->renderNewPwForm();
449: return;
450: }
451: if ($pw !== $pwRepeat) {
452: $this->_tpl->set('s', 'RESET_MESSAGE', i18n('Passwords don\'t match'));
453: $this->_tpl->set('s', 'RESET_LABEL', '');
454: $this->renderNewPwForm();
455: return;
456: }
457: if ((string) $_POST['user_pw'] === (string) $_GET['pw_reset']) {
458: $this->_tpl->set('s', 'RESET_MESSAGE', i18n('You may not use the confirmation token as password'));
459: $this->_tpl->set('s', 'RESET_LABEL', '');
460: $this->renderNewPwForm();
461: return;
462: }
463:
464:
465: $oApiUser = new cApiUser();
466: $oApiUser->loadUserByUsername($username);
467:
468: if (false === $oApiUser->isLoaded()) {
469:
470:
471: $this->_tpl->set('s', 'RESET_MESSAGE', i18n('New password has been set.'));
472: $this->_tpl->set('s', 'RESET_LABEL', '');
473: $this->_tpl->set('s', 'RESET_FORM', '');
474: return;
475: }
476:
477: $oPasswordRequest = new cApiUserPasswordRequestCollection();
478:
479:
480:
481:
482: if (null === ($requests = $this->_getCurrentRequests())) {
483:
484: $this->_tpl->set('s', 'RESET_MESSAGE', i18n('New password has been set.'));
485: $this->_tpl->set('s', 'RESET_LABEL', '');
486: $this->_tpl->set('s', 'RESET_FORM', '');
487: return;
488: }
489:
490:
491: $validUser = false;
492: foreach ($requests as $request) {
493:
494: if ($request->get('validation_token') === $_GET['pw_reset'])
495: {
496:
497: if ($oApiUser->get($oApiUser->primaryKey) === $request->get($oApiUser->primaryKey)) {
498:
499: $validUser = true;
500: }
501: }
502: }
503: if (false === $validUser) {
504:
505:
506: $this->_tpl->set('s', 'RESET_MESSAGE', i18n('New password has been set.'));
507: $this->_tpl->set('s', 'RESET_LABEL', '');
508: $this->_tpl->set('s', 'RESET_FORM', '');
509: return;
510: }
511:
512:
513: $res = $oApiUser->setPassword($pw);
514:
515: $msg = '';
516:
517: if (cApiUser::PASS_OK !== $res) {
518:
519: $msg = cApiUser::getErrorString($res);
520: $this->_tpl->set('s', 'RESET_MESSAGE', $msg);
521: $this->_tpl->set('s', 'RESET_LABEL', '');
522: $this->renderNewPwForm();
523: return;
524: }
525:
526: if (false !== $oApiUser->store()) {
527: $this->_tpl->set('s', 'RESET_LABEL', '');
528: $this->_tpl->set('s', 'RESET_FORM', '');
529:
530: $oPasswordRequest->deleteByUserId($oApiUser->get($oApiUser->primaryKey));
531: $msg = i18n('New password has been set.');
532: } else {
533:
534: $msg = i18n('An unknown problem occurred. Please contact your system administrator.');
535: }
536:
537:
538: $this->_tpl->set('s', 'RESET_MESSAGE', $msg);
539: }
540:
541: 542: 543: 544: 545: 546:
547: protected function _safePwResetRequest($token, DateTime $expiration) {
548: $oUserPwRequestCol = new cApiUserPasswordRequestCollection();
549: $oUserPwRequest = $oUserPwRequestCol->createNewItem();
550:
551:
552: $requestTime = new DateTime('now', new DateTimeZone('UTC'));
553: $oApiUser = new cApiUser();
554: $oApiUser->loadBy('username', $this->_username);
555:
556: $oUserPwRequest->set($oApiUser->primaryKey, $oApiUser->get($oApiUser->primaryKey));
557: $oUserPwRequest->set('request', $requestTime->format('Y-m-d H:i:s'));
558: $oUserPwRequest->set('expiration', $expiration->format('Y-m-d H:i:s'));
559: $oUserPwRequest->set('validation_token', $token);
560:
561:
562: return $oUserPwRequest->store();
563: }
564:
565: 566: 567: 568: 569:
570: protected function _submitMail($token) {
571: $cfg = cRegistry::getConfig();
572:
573: $token = (string) $token;
574:
575:
576: $msg = i18n("Dear CONTENIDO-User %s,\n\nA request to change your password for Content Management System CONTENIDO was made. ");
577: $msg .= i18n("Use the following URL to confirm the password change:\n\n");
578: $msg .= cRegistry::getBackendUrl() . '?pw_reset=';
579: $msg .= i18n("%s\n\nBest regards\n\nYour CONTENIDO sysadmin");
580: $mailBody = sprintf($msg, $this->_username, $token);
581:
582: $mailer = new cMailer();
583: $from = array(
584: $this->_sendermail => $this->_sendername
585: );
586:
587:
588: if ($cfg['php_settings']['default_charset'] != 'UTF-8') {
589: $subject = utf8_encode(conHtmlEntityDecode(stripslashes(i18n('Your new password for CONTENIDO Backend')), '', $cfg['php_settings']['default_charset']));
590: $body = utf8_encode(conHtmlEntityDecode($mailBody, '', $cfg['php_settings']['default_charset']));
591: } else {
592: $subject = conHtmlEntityDecode(stripslashes(i18n('Your new password for CONTENIDO Backend')));
593: $body = conHtmlEntityDecode($mailBody);
594: }
595:
596: $mailer->sendMail($from, $this->_email, $subject, $body);
597: }
598:
599: 600: 601: 602: 603:
604: protected function _generateToken() {
605:
606: $chars = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjkmnopqrstuvwxyz123456789";
607:
608: $password = "";
609:
610:
611: for ($i = 0; $i < $this->_tokenLength; $i++) {
612: $password .= $chars[rand(0, strlen($chars))];
613: }
614:
615: return $password;
616: }
617: }
618: