forked from openemr/openemr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
56ebe5c
commit d4ca70f
Showing
9 changed files
with
406 additions
and
354 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ | |
* @author Rod Roark <[email protected]> | ||
* @author Brady Miller <[email protected]> | ||
* @copyright Copyright (c) 2018 Rod Roark <[email protected]> | ||
* @copyright Copyright (c) 2018 Brady Miller <[email protected]> | ||
* @copyright Copyright (c) 2018-2019 Brady Miller <[email protected]> | ||
* @license https://github.com/openemr/openemr/blob/master/LICENSE CNU General Public License 3 | ||
*/ | ||
|
||
|
@@ -16,15 +16,18 @@ | |
|
||
use OpenEMR\Core\Header; | ||
|
||
function writeRow($method, $name) | ||
function writeRow($method, $name, $allowEdit = false) | ||
{ | ||
echo " <tr><td> "; | ||
echo text($method); | ||
echo " </td><td> "; | ||
echo text($name); | ||
echo " </td><td>"; | ||
echo "<input type='button' onclick='delclick(" . attr_js($method) . ", " . | ||
attr_js($name) . ")' value='" . xla('Delete') . "' />"; | ||
if ($allowEdit) { | ||
echo "<button type='button' class='btn btn-default btn-search' onclick='editclick(" . attr_js($method) . ")'>" . xlt('View') . "</button>"; | ||
} | ||
echo "<button type='button' class='btn btn-default btn-delete' onclick='delclick(" . attr_js($method) . ", " . | ||
attr_js($name) . ")'>" . xlt('Delete') . "</button>"; | ||
echo "</td></tr>\n"; | ||
} | ||
|
||
|
@@ -57,6 +60,16 @@ function delclick(mfamethod, mfaname) { | |
f.submit(); | ||
} | ||
|
||
function editclick(method) { | ||
top.restoreSession(); | ||
if (method == 'TOTP') { | ||
window.location.href = 'mfa_totp.php?action=reg1'; | ||
} | ||
else { | ||
alert(<?php echo xlj('Not yet implemented.'); ?>); | ||
} | ||
} | ||
|
||
function addclick(sel) { | ||
top.restoreSession(); | ||
if (sel.value) { | ||
|
@@ -102,8 +115,14 @@ function addclick(sel) { | |
<?php | ||
$res = sqlStatement("SELECT name, method FROM login_mfa_registrations WHERE " . | ||
"user_id = ? ORDER BY method, name", array($userid)); | ||
$disableNewTotp = false; | ||
while ($row = sqlFetchArray($res)) { | ||
writeRow($row['method'], $row['name']); | ||
if ($row['method'] == "TOTP") { | ||
$disableNewTotp = true; | ||
writeRow($row['method'], $row['name'], true); | ||
} else { | ||
writeRow($row['method'], $row['name']); | ||
} | ||
} | ||
?> | ||
</table> | ||
|
@@ -114,8 +133,12 @@ function addclick(sel) { | |
<br /> | ||
<select name='form_add' onchange='addclick(this)'> | ||
<option value=''><?php echo xlt('Add New...'); ?></option> | ||
<option value='U2F' ><?php echo xlt('U2F USB Device'); ?></option> | ||
<option value='TOTP'><?php echo xlt('TOTP Key'); ?></option> | ||
<option value='U2F'><?php echo xlt('U2F USB Device'); ?></option> | ||
<option value='TOTP' | ||
<?php echo ($disableNewTotp) ? 'title="' . xla('Only one TOTP Key can be set up per user') . '"' : ''; ?> | ||
<?php echo ($disableNewTotp) ? 'disabled' : ''; ?>> | ||
<?php echo xlt('TOTP Key'); ?> | ||
</option> | ||
</select> | ||
<input type='hidden' name='form_delete_method' value='' /> | ||
<input type='hidden' name='form_delete_name' value='' /> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,8 @@ | |
* @copyright Copyright (c) 2018 Brady Miller <[email protected]> | ||
* @license https://github.com/openemr/openemr/blob/master/LICENSE CNU General Public License 3 | ||
*/ | ||
|
||
|
||
require_once('../globals.php'); | ||
require_once("$srcdir/classes/Totp.class.php"); | ||
|
||
|
@@ -43,11 +45,6 @@ function docancel() { | |
window.location.href = 'mfa_registrations.php'; | ||
} | ||
|
||
function dodelete() { | ||
var f = document.forms[0]; | ||
f.action = 'mfa_registrations.php'; | ||
doregister('delete'); | ||
} | ||
</script> | ||
</head> | ||
<body class="body_top"> | ||
|
@@ -77,31 +74,18 @@ function dodelete() { | |
<div class="col-xs-12"> | ||
<?php if ($error == "auth") { ?> | ||
<div class="alert alert-danger login-failure m-1"> | ||
Invalid password | ||
<?php echo xlt('Invalid password'); ?> | ||
</div> | ||
<?php } ?> | ||
<p><?php echo xlt('In order to register your device, please provide your password'); ?></p> | ||
<table cellspacing="5"> | ||
<tr> | ||
<td> | ||
<label for="clearPass"><?php echo xlt('Password:'); ?> | ||
<label for="clearPass"><?php echo xlt('Password'); ?>: | ||
</td> | ||
<td> | ||
<input type="password" class="form-control" id="clearPass" name="clearPass" placeholder="<?php echo xlt('Password:'); ?>" > | ||
|
||
<?php | ||
// collect groups | ||
$res = sqlStatement("select distinct name from `groups`"); | ||
for ($iter = 0; $row = sqlFetchArray($res); $iter++) { | ||
$result[$iter] = $row; | ||
} | ||
|
||
if (count($result) == 1) { | ||
$resvalue = $result[0]{"name"}; | ||
echo "<input type='hidden' name='authProvider' value='" . attr($resvalue) . "' />\n"; | ||
} ?> | ||
|
||
</td> | ||
<input type="password" class="form-control" id="clearPass" name="clearPass" placeholder="<?php echo xla('Password'); ?>:" > | ||
</td> | ||
</tr> | ||
<tr> | ||
<td></td> | ||
|
@@ -117,8 +101,12 @@ function dodelete() { | |
<?php | ||
// step 2 is to validate password and display qr code | ||
} elseif ($action == 'reg2') { | ||
if (!verifyCsrfToken($_POST["csrf_token_form"])) { | ||
csrfNotVerified(); | ||
} | ||
|
||
// Redirect back to step 1 if user password is incorrect | ||
if (!validate_user_password($_SESSION["pc_username"], $_POST['clearPass'], $_POST['authProvider'])) { | ||
if (!validate_user_password($_SESSION['authUser'], $_POST['clearPass'], $_SESSION['authProvider'])) { | ||
header("Location: mfa_totp.php?action=reg1&error=auth"); | ||
exit(); | ||
} | ||
|
@@ -138,7 +126,7 @@ function dodelete() { | |
} | ||
|
||
// Generate a new QR code or existing QR code | ||
$googleAuth = new Totp($secret, $_SESSION["pc_username"]); | ||
$googleAuth = new Totp($secret, $_SESSION['authUser']); | ||
$qr = $googleAuth->generateQrCode(); | ||
|
||
|
||
|
@@ -149,36 +137,41 @@ function dodelete() { | |
?> | ||
<div class="row"> | ||
<div class="col-xs-12"> | ||
<p> | ||
<?php echo xlt('This will register a new TOTP key.'); ?> | ||
<?php echo xlt('Scan the following QR code with your preferred authenticator app.'); ?> | ||
</p> | ||
<img src="<?php echo attr($qr); ?>" height="150" /> | ||
<p> | ||
<?php echo xlt('Example authenticator apps include:'); ?> | ||
<?php if (!$doesExist) { ?> | ||
<p> | ||
<?php echo xlt('This will register a new TOTP key.'); ?> | ||
<?php echo xlt('Scan the following QR code with your preferred authenticator app.'); ?> | ||
</p> | ||
<?php } else { // $doesExist ?> | ||
<p> | ||
<?php echo xlt('Your current TOTP key QR code is displayed below.'); ?> | ||
</p> | ||
<?php } ?> | ||
<img src="<?php echo attr($qr); ?>" height="150" /> | ||
<p> | ||
<?php echo xlt('Example authenticator apps include:'); ?> | ||
<ul> | ||
<li><?php echo xla('Google Auth'); ?> | ||
<li><?php echo xlt('Google Auth'); ?> | ||
(<a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8"> | ||
<?php echo xla('ios'); ?> | ||
<?php echo xlt('ios'); ?> | ||
</a>, | ||
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en"> | ||
<?php echo xla('android'); ?> | ||
<?php echo xlt('android'); ?> | ||
</a>)</li> | ||
<li><?php echo xla('Authy'); ?> | ||
(<a href="https://itunes.apple.com/us/app/authy/id494168017?mt=8"><?php echo xla('ios'); ?></a>, <a href="https://play.google.com/store/apps/details?id=com.authy.authy&hl=en"><?php echo xla('android'); ?></a>)</li> | ||
<li><?php echo xlt('Authy'); ?> | ||
(<a href="https://itunes.apple.com/us/app/authy/id494168017?mt=8"><?php echo xlt('ios'); ?></a>, <a href="https://play.google.com/store/apps/details?id=com.authy.authy&hl=en"><?php echo xlt('android'); ?></a>)</li> | ||
</ul> | ||
</p> | ||
<p> | ||
<?php if ($doesExist) { ?> | ||
<input type='hidden' name='form_delete_method' value='TOTP' /> | ||
<input type='hidden' name='form_delete_name' value='App Based 2FA' /> | ||
<input type='button' value='<?php echo xla('Disable'); ?>' onclick='dodelete();' /> | ||
<?php } else { ?> | ||
</p> | ||
<?php if (!$doesExist) { ?> | ||
<p> | ||
<input type='button' value='<?php echo xla('Register'); ?>' onclick='doregister("reg3")' /> | ||
<?php } ?> | ||
|
||
<input type='button' value='<?php echo xla('Cancel'); ?>' onclick='docancel()' /> | ||
</p> | ||
<input type='button' value='<?php echo xla('Cancel'); ?>' onclick='docancel()' /> | ||
</p> | ||
<?php } else { // $doesExist ?> | ||
<p> | ||
<input type='button' value='<?php echo xla('Back'); ?>' onclick='docancel()' /> | ||
</p> | ||
<?php } ?> | ||
</div> | ||
</div> | ||
</div> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,9 +10,8 @@ | |
* @copyright Copyright (c) 2019 Brady Miller <[email protected]> | ||
* @license https://github.com/openemr/openemr/blob/master/LICENSE CNU General Public License 3 | ||
*/ | ||
|
||
use ParagonIE\MultiFactor\Vendor\GoogleAuth; | ||
use ParagonIE\Halite\Symmetric\Crypto as SymmetricCrypto; | ||
use Defuse\Crypto\Crypto; | ||
|
||
/** | ||
* Class Totp | ||
|
@@ -22,10 +21,6 @@ class Totp | |
|
||
/** @var bool|GoogleAuth */ | ||
private $_googleAuth = false; | ||
/** @var bool|string */ | ||
private $_qrFileName = false; | ||
/** @var bool|string - user's hashed password */ | ||
private $_hashedPass = false; | ||
/** @var bool|string - totp hashed secret */ | ||
private $_secret = false; | ||
/** @var string - issuer mentioned in the QR App */ | ||
|
@@ -44,7 +39,11 @@ public function __construct($secret = false, $username = '') | |
if ($secret) { | ||
$this->_secret = $secret; | ||
} else { | ||
$this->_secret = $this->_createRandString(16); | ||
// Shared key (per rfc6238 and rfc4226) should be 20 bytes (160 bits) and encoded in base32, which should | ||
// be 32 characters in base32 | ||
// Would be nice to use the produceRandomBytes() function and then encode to base32, but does not appear | ||
// to be a standard way to encode binary to base32 in php. | ||
$this->_secret = produceRandomString(32, "234567ABCDEFGHIJKLMNOPQRSTUVWXYZ"); | ||
} | ||
} | ||
|
||
|
@@ -55,21 +54,22 @@ public function __construct($secret = false, $username = '') | |
public function generateQrCode() | ||
{ | ||
if (class_exists('ParagonIE\MultiFactor\Vendor\GoogleAuth')) { | ||
|
||
// Generates a file with a PNG of the qr code | ||
$tempFilePath = $this->_getQrFilePath(); | ||
if (!empty($GLOBALS['temporary_files_dir'])) { | ||
$tempFilePath = tempnam($GLOBALS['temporary_files_dir'], "oer"); | ||
} else { | ||
$tempFilePath = tempnam(sys_get_temp_dir(), 'oer'); | ||
} | ||
$this->_getGoogleAuth()->makeQRCode(null, $tempFilePath, $this->_username, $this->_issuer); | ||
|
||
// Gets the image file data to return | ||
$imageInfo = getimagesize($tempFilePath); | ||
$data = base64_encode(file_get_contents($tempFilePath)); | ||
$image = sprintf('data:%s;base64,%s', $imageInfo['mime'], $data); | ||
$image = sprintf('data:%s;base64,%s', 'image/png', $data); | ||
|
||
// Delete image file before returning | ||
unlink($tempFilePath); | ||
|
||
return $image; | ||
|
||
} | ||
return false; | ||
} | ||
|
@@ -81,10 +81,8 @@ public function generateQrCode() | |
*/ | ||
public function validateCode($totp) | ||
{ | ||
if (class_exists('ParagonIE\MultiFactor\Vendor\GoogleAuth')) { | ||
|
||
if (class_exists('ParagonIE\MultiFactor\Vendor\GoogleAuth') && (!empty($this->_secret))) { | ||
return $this->_getGoogleAuth()->validateCode($totp, strtotime("now")); | ||
|
||
} | ||
return false; | ||
} | ||
|
@@ -98,18 +96,6 @@ public function getSecret() | |
return $this->_secret; | ||
} | ||
|
||
/** | ||
* Gets the file name of the string as a png | ||
* @return string | ||
*/ | ||
private function _getQrFilePath() | ||
{ | ||
if (!$this->_qrFileName) { | ||
$this->_qrFileName = md5($this->getSecret()); | ||
} | ||
return $this->_qrFileName.".png"; | ||
} | ||
|
||
/** | ||
* Gets the GoogleAuth object related this Totp | ||
* @return bool|GoogleAuth | ||
|
@@ -121,16 +107,4 @@ private function _getGoogleAuth() | |
} | ||
return $this->_googleAuth; | ||
} | ||
|
||
/** | ||
* Creates a random string of given length | ||
* @param $len - length of string | ||
* @return string | ||
*/ | ||
private function _createRandString($len) | ||
{ | ||
return substr(str_shuffle(str_repeat("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", $len)), 0, $len); | ||
} | ||
|
||
} | ||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.