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.
 
 
 
 
 
 

223 lines
6.4 KiB

<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id$
use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use PragmaRX\Google2FA\Google2FA;
function wikiplugin_totp_info()
{
return [
'name' => tra('Time-based One-time Password'),
'documentation' => 'PluginTOTP',
'description' => tra(
'Allows to generate Time-based One-time Password'
),
'prefs' => ['wikiplugin_totp'],
'extraparams' => true,
'validate' => 'all',
'params' => [
'secret' => [
'required' => false,
'name' => tra('Secret'),
'description' => tra(
'Secret key required to generate time-based one-time passwords'
),
],
'interval' => [
'required' => false,
'name' => tra('Interval'),
'description' => tra(
'Amount of seconds that a TOTP will be valid/refreshed'
),
],
'issuer' => [
'required' => false,
'name' => tra('Issuer'),
'description' => tra(
'Name of the application where the generated time-based one-time password will be use.'
),
],
],
];
}
function wikiplugin_totp($data, $params)
{
global $user, $page;
$sourcePerm = TikiLib::lib('tiki')->user_has_perm_on_object(
$user,
$page,
'wiki page',
'tiki_p_wiki_view_source'
);
$editPerm = TikiLib::lib('tiki')->user_has_perm_on_object(
$user,
$page,
'wiki page',
'tiki_p_edit'
);
$viewPerm = TikiLib::lib('tiki')->user_has_perm_on_object(
$user,
$page,
'wiki page',
'tiki_p_view'
);
if (! $viewPerm) {
return;
}
$headerlib = TikiLib::lib('header');
$headerlib->add_jsfile(
'vendor_bundled/vendor/npm-asset/zxing--library/umd/index.min.js'
);
$headerlib->add_jsfile('lib/jquery_tiki/wikiplugin-totp.js', true);
$tikilib = TikiLib::lib('tiki');
$info = $tikilib->get_page_info($page, true, true);
$defaults = [
'interval' => 30,
'issuer' => 'Unknown Application',
'id' => uniqid(),
'sourcePerm' => $sourcePerm,
];
$params = array_merge($defaults, $params);
if (
! isset($params['secret']) && isset($_REQUEST['action'])
&& $_REQUEST['action'] == 'add_totp'
&& isset($_REQUEST['secret'])
) {
$defaults = [
'period' => 30,
'issuer' => 'Unknown Application',
];
$data = array_merge($defaults, $_REQUEST);
addTOTPPlugin(
$page,
$user,
$data['secret'],
$data['period'],
$data['issuer']
);
echo json_encode([]);
die;
}
if (! isset($params['secret'])) { //try to request camera and id not possible show a form
if (! $editPerm) {
return;
}
$smarty = TikiLib::lib('smarty');
$smarty->assign($params);
$html = $smarty->fetch('wiki-plugins/wikiplugin_totp_scanner.tpl');
$html = preg_replace('/(\v|\s)+/', ' ', $html);
return '~np~' . html_entity_decode($html, ENT_HTML5, 'utf-8') . '~/np~';
}
$secretKey = $params['secret'];
if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'get_code') {
$google2fa = new Google2FA();
echo json_encode(
[
'code' => $google2fa->getCurrentOtp($secretKey),
'interval' => $params['interval'],
]
);
die;
}
$smarty = TikiLib::lib('smarty');
$smarty->assign(
'tfaSecretQR',
getSecretQR($secretKey, $params['interval'], $user, $params['issuer'])
);
$smarty->assign($params);
$html = $smarty->fetch('wiki-plugins/wikiplugin_totp.tpl');
$html = preg_replace('/(\v|\s)+/', ' ', $html);
return '~np~' . html_entity_decode($html, ENT_HTML5, 'utf-8') . '~/np~';
}
function getSecretQR($secretKey, $interval, $user, $issuer)
{
$google2fa = new Google2FA();
$google2fa->setKeyRegeneration($interval);
$g2faUrl = $google2fa->getQRCodeUrl(
$issuer,
$user,
$secretKey
);
if (extension_loaded('imagick')) {
$imageBackEnd = new ImagickImageBackEnd();
$imageType = 'png';
} else {
$imageBackEnd = new SvgImageBackEnd();
$imageType = 'svg+xml';
}
$writer = new Writer(
new ImageRenderer(
new RendererStyle(350),
$imageBackEnd
)
);
$tfaSecretQR = base64_encode($writer->writeString($g2faUrl));
return '<img src="data:image/' . $imageType . ';base64,'
. $tfaSecretQR . '"/>';
}
function addTOTPPlugin($pageName, $user, $secret, $interval, $issuer)
{
$widget = sprintf(
'{totp secret="%s" interval="%s" issuer="%s"}',
$secret,
$interval,
$issuer
);
$re = '/{totp(\s+\}|\})/mi';
$tikilib = TikiLib::lib('tiki');
$info = $tikilib->get_page_info($pageName, true, true);
$data = $info['data'];
$parserlib = TikiLib::lib('parser');
$parserlib->plugins_remove($data, $noparsed);
$noparsed['data'] = array_map(
function ($item) use ($parserlib, $widget) {
$plugins = $parserlib->getPlugins($item);
if (
! empty($plugins) && ! empty($plugins[0])
&& count($plugins[0]) == 5
&& strtolower($plugins[0][1]) == 'totp'
&& empty($plugins[0]['arguments'])
) {
return $widget;
}
return $item;
},
$noparsed['data']
);
$parserlib->plugins_replace($data, $noparsed);
$tikilib->update_page(
$pageName,
$data,
tra('TOTP added'),
$user,
$tikilib->get_ip_address()
);
return true;
}