mirror of https://github.com/MISP/MISP
Merge pull request #6214 from JakubOnderka/otp-encryption
fix: [otp] Allow to send encrypted OTP by mailpull/6262/head
commit
8f806c4f1b
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property User $User
|
||||
*/
|
||||
class UsersController extends AppController
|
||||
{
|
||||
public $newkey;
|
||||
|
@ -1678,71 +1681,73 @@ class UsersController extends AppController
|
|||
|
||||
public function email_otp()
|
||||
{
|
||||
$user = $this->Session->read('email_otp_user');
|
||||
if(empty($user)) {
|
||||
$this->redirect('login');
|
||||
}
|
||||
$redis = $this->User->setupRedis();
|
||||
$user_id = $user['id'];
|
||||
|
||||
if ($this->request->is('post') && isset($this->request->data['User']['otp'])) {
|
||||
$stored_otp = $redis->get('misp:otp:'.$user_id);
|
||||
if (!empty($stored_otp) && $this->request->data['User']['otp'] == $stored_otp) {
|
||||
// we invalidate the previously generated OTP
|
||||
$redis->delete('misp:otp:'.$user_id);
|
||||
// We login the user with CakePHP
|
||||
$this->Auth->login($user);
|
||||
$this->_postlogin();
|
||||
} else {
|
||||
$this->Flash->error(__("The OTP is incorrect or has expired"));
|
||||
$user = $this->Session->read('email_otp_user');
|
||||
if (empty($user)) {
|
||||
$this->redirect('login');
|
||||
}
|
||||
} else {
|
||||
// GET Request
|
||||
$redis = $this->User->setupRedisWithException();
|
||||
$user_id = $user['id'];
|
||||
|
||||
// We check for exceptions
|
||||
$exception_list = Configure::read('Security.email_otp_exceptions');
|
||||
if (!empty($exception_list)) {
|
||||
$exceptions = explode(",", $exception_list);
|
||||
foreach ($exceptions as &$exception) {
|
||||
if ($user['email'] == trim($exception)) {
|
||||
// We login the user with CakePHP
|
||||
$this->Auth->login($user);
|
||||
$this->_postlogin();
|
||||
if ($this->request->is('post') && isset($this->request->data['User']['otp'])) {
|
||||
$stored_otp = $redis->get('misp:otp:' . $user_id);
|
||||
if (!empty($stored_otp) && $this->request->data['User']['otp'] == $stored_otp) {
|
||||
// we invalidate the previously generated OTP
|
||||
$redis->del('misp:otp:' . $user_id);
|
||||
// We login the user with CakePHP
|
||||
$this->Auth->login($user);
|
||||
$this->_postlogin();
|
||||
} else {
|
||||
$this->Flash->error(__("The OTP is incorrect or has expired"));
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->loadModel('Server');
|
||||
|
||||
// Generating the OTP
|
||||
$digits = !empty(Configure::read('Security.email_otp_length')) ? Configure::read('Security.email_otp_length') : $this->Server->serverSettings['Security']['email_otp_length']['value'];
|
||||
$otp = "";
|
||||
for ($i=0; $i<$digits; $i++) {
|
||||
$otp.= random_int(0,9);
|
||||
}
|
||||
// We use Redis to cache the OTP
|
||||
$redis->set('misp:otp:'.$user_id, $otp);
|
||||
$validity = !empty(Configure::read('Security.email_otp_validity')) ? Configure::read('Security.email_otp_validity') : $this->Server->serverSettings['Security']['email_otp_validity']['value'];
|
||||
$redis->expire('misp:otp:'.$user_id, (int) $validity * 60);
|
||||
|
||||
// Email construction
|
||||
$body = !empty(Configure::read('Security.email_otp_text')) ? Configure::read('Security.email_otp_text') : $this->Server->serverSettings['Security']['email_otp_text']['value'];
|
||||
$body = str_replace('$misp', Configure::read('MISP.baseurl'), $body);
|
||||
$body = str_replace('$org', Configure::read('MISP.org'), $body);
|
||||
$body = str_replace('$contact', Configure::read('MISP.contact'), $body);
|
||||
$body = str_replace('$validity', $validity, $body);
|
||||
$body = str_replace('$otp', $otp, $body);
|
||||
$body = str_replace('$ip', $this->__getClientIP(), $body);
|
||||
$body = str_replace('$username', $user['email'], $body);
|
||||
$result = $this->User->sendEmail(array('User' => $user), $body, false, "[MISP] Email OTP");
|
||||
|
||||
if ( $result ) {
|
||||
$this->Flash->success(__("An email containing a OTP has been sent."));
|
||||
} else {
|
||||
$this->Flash->error(__("The email couldn't be sent, please reach out to your administrator."));
|
||||
}
|
||||
}
|
||||
}
|
||||
// GET Request
|
||||
|
||||
// We check for exceptions
|
||||
$exception_list = Configure::read('Security.email_otp_exceptions');
|
||||
if (!empty($exception_list)) {
|
||||
$exceptions = explode(",", $exception_list);
|
||||
foreach ($exceptions as $exception) {
|
||||
if ($user['email'] === trim($exception)) {
|
||||
// We login the user with CakePHP
|
||||
$this->Auth->login($user);
|
||||
$this->_postlogin();
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->loadModel('Server');
|
||||
|
||||
// Generating the OTP
|
||||
$digits = Configure::read('Security.email_otp_length') ?: $this->Server->serverSettings['Security']['email_otp_length']['value'];
|
||||
$otp = "";
|
||||
for ($i = 0; $i < $digits; $i++) {
|
||||
$otp .= random_int(0, 9);
|
||||
}
|
||||
// We use Redis to cache the OTP
|
||||
$redis->set('misp:otp:' . $user_id, $otp);
|
||||
$validity = Configure::read('Security.email_otp_validity') ?: $this->Server->serverSettings['Security']['email_otp_validity']['value'];
|
||||
$redis->expire('misp:otp:' . $user_id, (int)$validity * 60);
|
||||
|
||||
// Email construction
|
||||
$body = Configure::read('Security.email_otp_text') ?: $this->Server->serverSettings['Security']['email_otp_text']['value'];
|
||||
$body = str_replace('$misp', Configure::read('MISP.baseurl'), $body);
|
||||
$body = str_replace('$org', Configure::read('MISP.org'), $body);
|
||||
$body = str_replace('$contact', Configure::read('MISP.contact'), $body);
|
||||
$body = str_replace('$validity', $validity, $body);
|
||||
$body = str_replace('$otp', $otp, $body);
|
||||
$body = str_replace('$ip', $this->__getClientIP(), $body);
|
||||
$body = str_replace('$username', $user['email'], $body);
|
||||
|
||||
// Fetch user that contains also PGP or S/MIME keys for e-mail encryption
|
||||
$userForSendMail = $this->User->getUserById($user_id);
|
||||
$result = $this->User->sendEmail($userForSendMail, $body, false, "[MISP] Email OTP");
|
||||
|
||||
if ($result) {
|
||||
$this->Flash->success(__("An email containing a OTP has been sent."));
|
||||
} else {
|
||||
$this->Flash->error(__("The email couldn't be sent, please reach out to your administrator."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to determine the IP of a client (proxy aware)
|
||||
|
|
|
@ -366,6 +366,10 @@ class SendEmail
|
|||
throw new SendEmailException('Emailing is currently disabled on this instance.');
|
||||
}
|
||||
|
||||
if (!isset($user['User'])) {
|
||||
throw new InvalidArgumentException("Invalid user model provided.");
|
||||
}
|
||||
|
||||
// Check if the e-mail can be encrypted
|
||||
$canEncryptGpg = isset($user['User']['gpgkey']) && !empty($user['User']['gpgkey']);
|
||||
$canEncryptSmime = isset($user['User']['certif_public']) && !empty($user['User']['certif_public']) && Configure::read('SMIME.enabled');
|
||||
|
|
|
@ -584,36 +584,38 @@ class User extends AppModel
|
|||
return $user;
|
||||
}
|
||||
|
||||
// get the current user and rearrange it to be in the same format as in the auth component
|
||||
public function getAuthUser($id)
|
||||
/**
|
||||
* @param int $id
|
||||
* @return array|null
|
||||
*/
|
||||
public function getUserById($id)
|
||||
{
|
||||
if (empty($id)) {
|
||||
throw new NotFoundException('Invalid user ID.');
|
||||
}
|
||||
$conditions = array('User.id' => $id);
|
||||
$user = $this->find(
|
||||
return $this->find(
|
||||
'first',
|
||||
array(
|
||||
'conditions' => $conditions,
|
||||
'conditions' => array('User.id' => $id),
|
||||
'recursive' => -1,
|
||||
'contain' => array(
|
||||
'Organisation',
|
||||
'Role',
|
||||
'Server',
|
||||
'UserSetting'
|
||||
'UserSetting',
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// get the current user and rearrange it to be in the same format as in the auth component
|
||||
public function getAuthUser($id)
|
||||
{
|
||||
$user = $this->getUserById($id);
|
||||
if (empty($user)) {
|
||||
return $user;
|
||||
}
|
||||
// Rearrange it a bit to match the Auth object created during the login
|
||||
$user['User']['Role'] = $user['Role'];
|
||||
$user['User']['Organisation'] = $user['Organisation'];
|
||||
$user['User']['Server'] = $user['Server'];
|
||||
$user['User']['UserSetting'] = $user['UserSetting'];
|
||||
unset($user['Organisation'], $user['Role'], $user['Server']);
|
||||
return $user['User'];
|
||||
return $this->rearrangeToAuthForm($user);
|
||||
}
|
||||
|
||||
// get the current user and rearrange it to be in the same format as in the auth component
|
||||
|
@ -624,11 +626,7 @@ class User extends AppModel
|
|||
if (empty($user)) {
|
||||
return $user;
|
||||
}
|
||||
// Rearrange it a bit to match the Auth object created during the login
|
||||
$user['User']['Role'] = $user['Role'];
|
||||
$user['User']['Organisation'] = $user['Organisation'];
|
||||
$user['User']['Server'] = $user['Server'];
|
||||
return $user['User'];
|
||||
return $this->rearrangeToAuthForm($user);
|
||||
}
|
||||
|
||||
public function getAuthUserByExternalAuth($auth_key)
|
||||
|
@ -649,11 +647,28 @@ class User extends AppModel
|
|||
if (empty($user)) {
|
||||
return $user;
|
||||
}
|
||||
// Rearrange it a bit to match the Auth object created during the login
|
||||
return $this->rearrangeToAuthForm($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* User model is a mess. Sometimes it is necessary to convert User model to form that is created during the login
|
||||
* process. This method do that work for you.
|
||||
*
|
||||
* @param array $user
|
||||
* @return array
|
||||
*/
|
||||
public function rearrangeToAuthForm(array $user)
|
||||
{
|
||||
if (!isset($user['User'])) {
|
||||
throw new InvalidArgumentException('Invalid user model provided.');
|
||||
}
|
||||
|
||||
$user['User']['Role'] = $user['Role'];
|
||||
$user['User']['Organisation'] = $user['Organisation'];
|
||||
$user['User']['Server'] = $user['Server'];
|
||||
unset($user['Organisation'], $user['Role'], $user['Server']);
|
||||
if (isset($user['UserSetting'])) {
|
||||
$user['User']['UserSetting'] = $user['UserSetting'];
|
||||
}
|
||||
return $user['User'];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue