mirror of https://github.com/MISP/MISP
chg: [security] allow creation of TOTP token
parent
6caccac94d
commit
61573392ea
|
@ -746,6 +746,7 @@ class ACLComponent extends Component
|
|||
'edit' => array('self_management_enabled'),
|
||||
'email_otp' => array('*'),
|
||||
'totp' => array('*'),
|
||||
'totp_new' => array('*'),
|
||||
'searchGpgKey' => array('*'),
|
||||
'fetchGpgKey' => array('*'),
|
||||
'histogram' => array('*'),
|
||||
|
|
|
@ -1207,6 +1207,10 @@ class UsersController extends AppController
|
|||
return $this->redirect('totp');
|
||||
}
|
||||
}
|
||||
// FIXME chri - implement a way for the user to login the first time when TOTP is required for all users,
|
||||
// - redirect to TOTP generation page just after login (or any page requested)
|
||||
// - for new account creations
|
||||
// - for when the org admin wants to allow the user to generate a new TOTP
|
||||
}
|
||||
}
|
||||
// if instance requires email OTP
|
||||
|
@ -1782,7 +1786,51 @@ class UsersController extends AppController
|
|||
} else {
|
||||
// GET Request, just show the form
|
||||
}
|
||||
}
|
||||
|
||||
public function totp_new($id = null)
|
||||
{
|
||||
// FIXME chri - reuse $id to load user if (org)site admin
|
||||
$user = $this->Auth->user();
|
||||
// do not allow this page to be accessed if the current already has a TOTP. Just redirect to the users details page with a Flash->error()
|
||||
if ($user['totp']) {
|
||||
$this->Flash->error(__("Your account already has an TOTP. Please contact your organisational administrator to change or delete it."));
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
|
||||
$secret = $this->Session->read('totp_secret'); // Reload secret from session.
|
||||
if ($secret) {
|
||||
$otp = \OTPHP\TOTP::create($secret);
|
||||
} else {
|
||||
$otp = \OTPHP\TOTP::create();
|
||||
$secret = $otp->getSecret();
|
||||
$this->Session->write('totp_secret', $secret); // Store in session, this is to keep the same QR code even if the page refreshes.
|
||||
}
|
||||
if ($this->request->is('post') && isset($this->request->data['User']['otp'])) {
|
||||
$otp_now = $otp->now();
|
||||
if (trim($this->request->data['User']['otp']) == $otp_now) {
|
||||
// we know the user can generate TOTP tokens, save the new TOTP to the database
|
||||
$this->User->id = $user['id'];
|
||||
$this->User->saveField('totp', $secret);
|
||||
$this->Flash->info(__('The OTP is correct and now active for your account.'));
|
||||
$this->redirect(array('controller' => 'events', 'action'=> 'index'));
|
||||
} else {
|
||||
$this->Flash->error(__("The OTP is incorrect or has expired."));
|
||||
}
|
||||
} else {
|
||||
// GET Request, just show the form
|
||||
}
|
||||
// generate QR code with the secret
|
||||
$renderer = new \BaconQrCode\Renderer\ImageRenderer(
|
||||
new \BaconQrCode\Renderer\RendererStyle\RendererStyle(200),
|
||||
new \BaconQrCode\Renderer\Image\SvgImageBackEnd()
|
||||
);
|
||||
$writer = new \BaconQrCode\Writer($renderer);
|
||||
$qrcode = $writer->writeString('otpauth://totp/' . Configure::read('MISP.org') . ' MISP (' . $user['email'] . ')?secret=' . $secret);
|
||||
$writer = preg_replace('/^.+\n/', '', $qrcode); // ignore first <?xml version line
|
||||
|
||||
$this->set('qrcode', $qrcode);
|
||||
$this->set('secret', $secret);
|
||||
}
|
||||
|
||||
public function email_otp()
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php echo $this->Flash->render(); ?>
|
||||
|
||||
<div class="actions sideMenu">
|
||||
<div style="padding: 10px;">
|
||||
<p><?php echo __("Generate a new TOTP token to login. (Time-Based One-Time Password)");?></p>
|
||||
<p><?php echo __("Please scan the following QR code with your TOTP application.");?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<?php
|
||||
// FIXME chri - make it visually attractive
|
||||
echo $qrcode;
|
||||
?>
|
||||
<p>Alternatively you can enter the following secret in your TOTP application: <pre><?php echo $secret; ?></pre>
|
||||
<?php
|
||||
|
||||
echo $this->element('/genericElements/Form/genericForm', array(
|
||||
"form" => $this->Form,
|
||||
"data" => array(
|
||||
"title" => __("Validate your One Time Password"),
|
||||
"fields" => array(
|
||||
array(
|
||||
"field" => "otp",
|
||||
"label" => __("One Time Password"),
|
||||
"type" => "text",
|
||||
"placeholder" => __("Enter your OTP code here"),
|
||||
)
|
||||
),
|
||||
"submit" => array (
|
||||
"action" => "totp",
|
||||
),
|
||||
)));
|
||||
?>
|
||||
</div>
|
|
@ -23,7 +23,9 @@ $boolean = sprintf(
|
|||
'<span class="%s">%s</span>',
|
||||
$isTotp ? 'label label-success label-padding' : 'label label-important label-padding',
|
||||
$isTotp ? __('Yes') : __('No'));
|
||||
$totpHtml = $boolean . ''. __('Generate TOTP'); // FIXME chri - create link to generate a new TOTP, only save in DB after validation
|
||||
$totpHtml = $boolean;
|
||||
$totpHtml .= ($isTotp ? '' : $this->Html->link(__('Generate'), array('action' => 'totp_new', $user['User']['id'])));
|
||||
$totpHtml .= ($admin_view && $isTotp ? ' ' . __('Delete') : ''); // FIXME chri - only allow delete for (org)admin
|
||||
|
||||
|
||||
$table_data = [
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
"kamisama/cake-resque": "4.1.2",
|
||||
"pear/crypt_gpg": "1.6.7",
|
||||
"monolog/monolog": "1.24.0",
|
||||
"spomky-labs/otphp": "^10.0"
|
||||
"spomky-labs/otphp": "^10.0",
|
||||
"bacon/bacon-qr-code": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8",
|
||||
|
@ -40,7 +41,8 @@
|
|||
"lstrojny/fxmlrpc": "Required for supervisorphp/supervisor XML-RPC requests",
|
||||
"guzzlehttp/guzzle": "Required for supervisorphp/supervisor XML-RPC requests",
|
||||
"php-http/message": "Required for supervisorphp/supervisor XML-RPC requests",
|
||||
"spomky-labs/otphp": "Required for strong authentication with TOTP"
|
||||
"spomky-labs/otphp": "Required for strong authentication with TOTP",
|
||||
"bacon/bacon-qr-code": "Required for strong authentication with TOTP"
|
||||
},
|
||||
"config": {
|
||||
"vendor-dir": "Vendor",
|
||||
|
|
Loading…
Reference in New Issue