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.

130 lines
3.9 KiB

<?php declare(strict_types=1);
namespace BradyMcD\TAATP\Workflow;
use BradyMcD\TAATP\Required\PersistenceInterface;
use BradyMcD\TAATP\Required\RequestInterface;
use BradyMcD\TAATP\HashConfig;
use BradyMcD\TAATP\ModuleConfig;
use BradyMcD\TAATP\WorkflowInterface;
use BradyMcD\TAATP\URI\Otpauth;
use BradyMcD\RFC\_6238;
use chillerlan\QRCode\QRCode;
/** @SuppressWarnings(PHPMD.ShortVariable)*/
class UserManagement implements WorkflowInterface
{
public function __construct(
private PersistenceInterface $db,
private RequestInterface $request,
private mixed $userIndex,
private ModuleConfig $moduleConfig,
private HashConfig $hashConfig,
)
{
}
function view()
{
echo $this->emitStr();
}
private function viewEnrollForm(): string
{
$html = "<div id=\"enroll\"><form %frm>";
$html .= $this->moduleConfig.csrf->emitStr();
$html .= "<p>To add an authenticator to your account, scan the QR code</p>";
$html .= "<img src=%qr alt=\"qr-code\" height=256px width=256px/>";
$html .= "<label for=\"totp_challenge\">Enter the authentication code:</label>";
$html .= "<input name=\"totp_challenge\" id=\"totp_challenge\" type=\"text\" >";
$html .= "<input type=\"submit\" value=\"Submit\" />";
$html .= "</form></div>";
$otpauthURI = new Otpauth(
$this->db->userString($this->userIndex),
"taaatp",
$this->hashConfig.hash->keygen(),
$this->hashConfig.hash->hashType(),
6,
30
);
$provisioningUri = $otpauthURI->uriString();
$persistentUri = $otpauthURI->uriStringExplicit();
$this->moduleConfig.session->store("secret", $persistentUri);
$values = [
"%frm" => $this->request->formProps("enroll"),
"%qr" => (new QRCode)->render($provisioningUri),
];
return \str_replace(\array_keys($values), $values, $html);
}
private function viewUnenrollForm(): string
{
$html = "<div id=\"unenroll\"><form %frm>";
$html .= $this->moduleConfig.csrf->emitStr();
$html .= "<label for=\"totp_challenge\">To de-register your authenticator enter the current authentication code:</label>";
$html .= "<intput name=\"totp_challenge\" id=\"totp_challenge\" type\"text\" />";
$html .= "<input type=\"submit\" value=\"Submit\" />";
$html .= "</form></div>";
$values = [
"%frm" => $this->request->formProps("unenroll"),
];
return \str_replace(\array_keys($values), $values, $html);
}
function emitStr(): string
{
if (\is_null($this->db->getSecret($this->userIndex)))
{
return $this->viewEnrollForm();
}
return $this->viewUnenrollForm();
}
function response(): bool
{
if (!$this->moduleConfig.csrf->match())
{
return false;
}
$pUri = $this->db->getSecret($this->userIndex);
$enrollFlag = \is_null($pUri);
$enrollFlag && $pUri = $this->moduleConfig.session->get('secret');
echo "Recovered pURI for a " . ($enrollFlag)?"new user":"returning user";
$totp = _6238(
$pUri->getSecret(),
$pUri.period,
$enrollFlag? 0:$this->db->getLastTime($this->userIndex),
2,
$pUri.digits,
$this->hashConfig.clock,
$this->hashConfig.algorithm
);
$flag = $totp.validate($this->request->getResp("totp_challenge"));
if($flag && $enrollFlag)
{
$this->db->storeLastTime($this->userIndex, $flag);
$this->db->storeSecret($this->userIndex, $pUri);
return true;
}
else if($flag)
{
$this->db->stripSecret($this->userIndex);
return true;
}
return false;
}
}
?>