diff --git a/application/Espo/Controllers/User.php b/application/Espo/Controllers/User.php index 17a7138d01..e6d450efac 100644 --- a/application/Espo/Controllers/User.php +++ b/application/Espo/Controllers/User.php @@ -25,11 +25,12 @@ namespace Espo\Controllers; use \Espo\Core\Exceptions\Error; use \Espo\Core\Exceptions\NotFound; use \Espo\Core\Exceptions\Forbidden; +use \Espo\Core\Exceptions\BadRequest; class User extends \Espo\Core\Controllers\Record -{ +{ public function actionAcl($params, $data, $request) - { + { $userId = $request->get('id'); if (empty($userId)) { throw new Error(); @@ -46,12 +47,28 @@ class User extends \Espo\Core\Controllers\Record $acl = new \Espo\Core\Acl($user, $this->getConfig(), $this->getContainer()->get('fileManager'), $this->getMetadata()); - return $acl->toArray(); + return $acl->toArray(); } public function actionChangeOwnPassword($params, $data) { return $this->getService('User')->changePassword($this->getUser()->id, $data['password']); } + + public function actionPasswordChangeRequest($params, $data, $request) + { + if (!$request->isPost()) { + throw new Forbidden(); + } + + if (empty($data['userName']) || empty($data['emailAddress'])) { + throw new BadRequest(); + } + + $userName = $data['userName']; + $emailAddress = $data['emailAddress']; + + return $this->getService('User')->passwordChangeRequest($userName, $emailAddress); + } } diff --git a/application/Espo/Entities/PasswordChangeRequest.php b/application/Espo/Entities/PasswordChangeRequest.php new file mode 100644 index 0000000000..425b0c8f6b --- /dev/null +++ b/application/Espo/Entities/PasswordChangeRequest.php @@ -0,0 +1,29 @@ +dependencies[] = 'mailSender'; - $this->dependencies[] = 'language'; + $this->dependencies[] = 'container'; } protected $internalFields = array('password'); protected function getMailSender() { - return $this->injections['mailSender']; + return $this->getContainer()->get('mailSender'); } protected function getLanguage() { - return $this->injections['language']; + return $this->getContainer()->get('language'); + } + + protected function getContainer() + { + return $this->injections['container']; } public function getEntity($id) @@ -91,6 +97,73 @@ class User extends Record return true; } + public function passwordChangeRequest($userName, $emailAddress) + { + $user = $this->getEntityManager()->getRepository('User')->where(array( + 'userName' => $userName, + 'emailAddress' => $emailAddress + ))->findOne(); + + if (empty($user)) { + throw new NotFound(); + } + + $userId = $user->id; + + $passwordChangeRequest = $this->getEntityManager()->getRepository('PasswordChangeRequest')->where(array( + 'userId' => $userId + ))->findOne(); + if ($passwordChangeRequest) { + throw new Forbidden(); + } + + $requestId = uniqid(); + + $passwordChangeRequest = $this->getEntityManager()->getEntity('PasswordChangeRequest'); + $passwordChangeRequest->set(array( + 'userId' => $userId, + 'requestId' => $requestId + )); + + $this->sendChangePasswordLink($requestId, $emailAddress); + + $this->getEntityManager()->saveEntity($passwordChangeRequest); + + if (!$passwordChangeRequest->id) { + throw new Error(); + } + + $dt = new \DateTime(); + $dt->add(\DateTimeInterval('P'. self::PASSWORD_CHANGE_REQUEST_LIFETIME . 'i')); + + $job->set(array( + 'serviceName' => 'User', + 'method' => 'removeChangePasswordRequestJob', + 'data' => json_encode(array( + 'id' => $passwordChangeRequest->id, + )), + 'executeTime' => $dt->format('Y-m-d H:i:s') , + )); + + $this->getEntityManager()->saveEntity($job); + + return true; + } + + public function removeChangePasswordRequestJob($data) + { + $id = $data->id; + if (empty($id)) { + return; + } + + $p = $this->getEntityManager()->getEntity('PasswordChangeRequest', $data->id); + if ($p) { + $this->getEntityManager()->removeEntity($p); + } + return true; + } + protected function hashPassword($password) { $config = $this->getConfig(); @@ -168,6 +241,35 @@ class User extends Record $this->getMailSender()->send($email); } + protected function sendChangePasswordLink($requestId, $emailAddress, Entity $user = null) + { + if (empty($emailAddress)) { + return; + } + + $email = $this->getEntityManager()->getEntity('Email'); + + if (!$this->getConfig()->get('smtpServer')) { + return; + } + + $subject = $this->getLanguage()->translate('passwordChangeLinkEmailSubject', 'messages', 'User'); + $body = $this->getLanguage()->translate('passwordChangeLinkEmailBody', 'messages', 'User'); + + $link = $this->getConfig()->get('siteUrl') . '?entryPoint=changePassword&id=' . $requestId; + + $body = str_replace('{link}', $this->getConfig()->get('siteUrl'), $body); + + $email->set(array( + 'subject' => $subject, + 'body' => $body, + 'isHtml' => false, + 'to' => $emailAddress + )); + + $this->getMailSender()->send($email); + } + public function deleteEntity($id) { if ($id == 'system') { diff --git a/frontend/client/res/templates/login.tpl b/frontend/client/res/templates/login.tpl index f71541905a..5561e60645 100644 --- a/frontend/client/res/templates/login.tpl +++ b/frontend/client/res/templates/login.tpl @@ -1,5 +1,5 @@