You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
13 KiB
259 lines
13 KiB
<?php |
|
|
|
/** |
|
* App Based TOTP Support |
|
* |
|
* @package OpenEMR |
|
* @link http://www.open-emr.org |
|
* @author Anthony Zullo <anthonykzullo@gmail.com> |
|
* @author Rod Roark <rod@sunsetsystems.com> |
|
* @author Brady Miller <brady.g.miller@gmail.com> |
|
* @copyright Copyright (c) 2019 Anthony Zullo <anthonykzullo@gmail.com> |
|
* @copyright Copyright (c) 2018 Rod Roark <rod@sunsetsystems.com> |
|
* @copyright Copyright (c) 2018-2019 Brady Miller <brady.g.miller@gmail.com> |
|
* @license https://github.com/openemr/openemr/blob/master/LICENSE CNU General Public License 3 |
|
*/ |
|
|
|
// Set $sessionAllowWrite to true to prevent session concurrency issues during authorization related code |
|
$sessionAllowWrite = true; |
|
require_once('../globals.php'); |
|
require_once("$srcdir/classes/Totp.class.php"); |
|
require_once("$srcdir/options.inc.php"); |
|
|
|
use OpenEMR\Common\Auth\AuthUtils; |
|
use OpenEMR\Common\Crypto\CryptoGen; |
|
use OpenEMR\Common\Csrf\CsrfUtils; |
|
use OpenEMR\Core\Header; |
|
use OpenEMR\OeUI\OemrUI; |
|
|
|
$userid = $_SESSION['authUserID']; |
|
$action = $_REQUEST['action']; |
|
$user_name = getUserIDInfo($userid); |
|
$user_full_name = $user_name['fname'] . " " . $user_name['lname']; |
|
|
|
?> |
|
<html> |
|
<head> |
|
<?php Header::setupHeader(); ?> |
|
<title><?php echo xlt('TOTP Registration'); ?></title> |
|
<script> |
|
|
|
function doregister(step, error) { |
|
var f = document.forms[0]; |
|
f.action.value = step; |
|
if (error) { |
|
f.error.value = error; |
|
} |
|
f.action.value = step; |
|
top.restoreSession(); |
|
f.submit(); |
|
} |
|
|
|
function docancel() { |
|
var redirectUrl = 'mfa_registrations.php'; |
|
window.location.href = 'mfa_registrations.php'; |
|
} |
|
|
|
$(function () { |
|
$('#clearPass').focus(); |
|
}); |
|
</script> |
|
<style> |
|
p { |
|
text-align: center |
|
} |
|
.alert-msg { |
|
font-size:100%; |
|
font-weight:700; |
|
} |
|
</style> |
|
<?php |
|
$arrOeUiSettings = array( |
|
'heading_title' => xl('Register Time Based One Time Password Key') . " - " . xl('TOTP'), |
|
'include_patient_name' => false, |
|
'expandable' => false, |
|
'expandable_files' => array(),//all file names need suffix _xpd |
|
'action' => "",//conceal, reveal, search, reset, link or back |
|
'action_title' => "", |
|
'action_href' => "",//only for actions - reset, link or back |
|
'show_help_icon' => false, |
|
'help_file_name' => "" |
|
); |
|
$oemr_ui = new OemrUI($arrOeUiSettings); |
|
?> |
|
</head> |
|
<body class="body_top"> |
|
<div id="container_div" class="<?php echo $oemr_ui->oeContainer();?>"> |
|
<?php |
|
// If current step is reg1 or reg2, display the header |
|
if ($action == 'reg1' || $action == 'reg2') { ?> |
|
<div id="container_div" class="<?php echo $oemr_ui->oeContainer();?>"> |
|
<div class="row"> |
|
<div class="col-sm-12"> |
|
<?php echo $oemr_ui->pageHeading() . "\r\n"; ?> |
|
</div> |
|
</div> |
|
<?php |
|
} ?> <div class="row"> |
|
<div class="col-sm-12"> |
|
<form method='post' class="form-horizontal" action='mfa_totp.php' onsubmit='return top.restoreSession()'> |
|
<input type="hidden" name="csrf_token_form" value="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" /> |
|
|
|
|
|
|
|
|
|
<?php |
|
// step 1 is to verify the password |
|
if ($action == 'reg1') { |
|
$error = (isset($_GET["error"])) ? $_GET["error"] : false; |
|
?> |
|
<div> |
|
<fieldset> |
|
<legend><?php echo xlt('Provide Password for') . " " . text($user_full_name); ?></legend> |
|
<div class="row"> |
|
<div class="col-sm-12"> |
|
<?php if ($error == "auth") { ?> |
|
<div class="alert alert-danger alert-msg login-failure m-1"> |
|
<?php echo xlt('Invalid password'); ?> |
|
</div> |
|
<?php } ?> |
|
<p><?php echo xlt('In order to register your device, please provide your OpenEMR login password'); ?></p> |
|
<div class="col-sm-4 offset-sm-4"> |
|
<input type="password" class="form-control" id="clearPass" name="clearPass" placeholder="<?php echo xla('Password'); ?>:" > |
|
</div> |
|
</div> |
|
</div> |
|
</fieldset> |
|
<div class="form-group clearfix"> |
|
<div class="col-sm-12 text-left position-override"> |
|
<button type="button" class="btn btn-secondary btn-save" value="<?php echo xla('Submit'); ?>" onclick="doregister('reg2')"><?php echo xlt('Submit'); ?></button> |
|
<button type="button" class="btn btn-link btn-cancel" value="<?php echo xla('Cancel'); ?>" onclick="docancel()" ><?php echo xlt('Cancel'); ?></button> |
|
</div> |
|
</div> |
|
</div> |
|
<?php |
|
// step 2 is to validate password and display qr code |
|
} elseif ($action == 'reg2') { |
|
if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) { |
|
CsrfUtils::csrfNotVerified(); |
|
} |
|
|
|
// Redirect back to step 1 if user password is incorrect |
|
if (!(new AuthUtils())->confirmPassword($_SESSION['authUser'], $_POST['clearPass'])) { |
|
header("Location: mfa_totp.php?action=reg1&error=auth"); |
|
exit(); |
|
} |
|
|
|
// Determines whether existing TOTP method exists already |
|
$existingSecret = privQuery( |
|
"SELECT var1 FROM login_mfa_registrations WHERE " . |
|
"`user_id` = ? AND `method` = 'TOTP'", |
|
array($userid) |
|
); |
|
if (empty($existingSecret['var1'])) { |
|
$secret = false; |
|
$doesExist = false; |
|
} else { |
|
$cryptoGen = new CryptoGen(); |
|
$secret = $cryptoGen->decryptStandard($existingSecret['var1']); |
|
$doesExist = true; |
|
} |
|
|
|
// Generate a new QR code or existing QR code |
|
$googleAuth = new Totp($secret, $_SESSION['authUser']); |
|
$qr = $googleAuth->generateQrCode(); |
|
|
|
|
|
// if secret did not exist previously, stores secret in session variable for saving |
|
if (!$doesExist) { |
|
$_SESSION['totpSecret'] = $googleAuth->getSecret(); |
|
} |
|
?> |
|
<fieldset> |
|
<legend><?php echo xlt('Register TOTP Key for') . " " . text($user_full_name); ?></legend> |
|
<div class="row"> |
|
<div class="col-sm-12"> |
|
<?php if (!$doesExist) { ?> |
|
<p> |
|
<?php echo xlt('Scan the following QR code with your preferred authenticator app to register a new TOTP key.'); ?> |
|
</p> |
|
<?php } else { // $doesExist ?> |
|
<p> |
|
<?php echo xlt('Your current TOTP key QR code is displayed below.'); ?> |
|
</p> |
|
<?php } ?> |
|
<br /> |
|
<img src="<?php echo attr($qr); ?>" class="img-responsive center-block" style="height:200px !Important"/> |
|
<br /> |
|
<p><?php echo xlt('Example authenticator apps include'); ?></p>: |
|
<div class="col-sm-4 offset-sm-4"> |
|
<ul> |
|
<li><?php echo xlt('Google Auth'); ?> |
|
(<a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8" target="_blank" rel="noopener"> |
|
<?php echo xlt('ios'); ?> |
|
</a>, |
|
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en" target="_blank" rel="noopener"> |
|
<?php echo xlt('android'); ?> |
|
</a>)</li> |
|
<li><?php echo xlt('Authy'); ?> |
|
(<a href="https://itunes.apple.com/us/app/authy/id494168017?mt=8" target="_blank" rel="noopener"><?php echo xlt('ios'); ?></a>, <a href="https://play.google.com/store/apps/details?id=com.authy.authy&hl=en" target="_blank" rel="noopener"><?php echo xlt('android'); ?></a>)</li> |
|
</ul> |
|
</div> |
|
</div> |
|
</div> |
|
</fieldset> |
|
<div class="form-group clearfix"> |
|
<div class="col-sm-12 text-left position-override"> |
|
<?php if (!$doesExist) { ?> |
|
<button type="button" class="btn btn-secondary btn-save" value="<?php echo xla('Register'); ?>" onclick="doregister('reg3')"><?php echo xlt('Register'); ?></button> |
|
<button type="button" class="btn btn-link btn-cancel" value="<?php echo xla('Cancel'); ?>" onclick="docancel()" ><?php echo xlt('Cancel'); ?></button> |
|
<?php } else { // $doesExist ?> |
|
<button type="button" class="btn btn-link btn-back" value="<?php echo xla('Back'); ?>" onclick="docancel()" ><?php echo xlt('Back'); ?></button> |
|
<?php } ?> |
|
</div> |
|
</div> |
|
</div> |
|
<?php |
|
// step 3 is to save the qr code |
|
} elseif ($action == 'reg3') { |
|
if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) { |
|
CsrfUtils::csrfNotVerified(); |
|
} |
|
|
|
echo "<script>\n"; |
|
|
|
// Verify that no TOTP method exists already |
|
$row = privQuery( |
|
"SELECT COUNT(*) AS count FROM login_mfa_registrations WHERE " . |
|
"`user_id` = ? AND `method` = 'TOTP'", |
|
array($userid) |
|
); |
|
|
|
|
|
if (empty($row['count']) && isset($_SESSION['totpSecret'])) { |
|
$cryptoGen = new CryptoGen(); |
|
privStatement( |
|
"INSERT INTO login_mfa_registrations " . |
|
"(`user_id`, `method`, `name`, `var1`, `var2`) VALUES " . |
|
"(?, 'TOTP', 'App Based 2FA', ?, '')", |
|
array($userid, $cryptoGen->encryptStandard($_SESSION['totpSecret'])) |
|
); |
|
unset($_SESSION['totpSecret']); |
|
} else { |
|
echo " alert(" . xlj('TOTP Method already exists and is enabled. Try again.') . ");\n"; |
|
} |
|
|
|
echo "window.location.href = 'mfa_registrations.php';\n"; |
|
echo "</script>\n"; |
|
} |
|
?> |
|
|
|
<input type='hidden' name='action' value='' /> |
|
<input type='hidden' name='error' value='' /> |
|
</form> |
|
</div> |
|
</div> |
|
</div><!--end of container div --> |
|
<?php $oemr_ui->oeBelowContainerDiv();?> |
|
</body> |
|
</html>
|
|
|