shell bypass 403
<?php
/**
* @package Joomla.Plugin
* @subpackage System.Webauthn
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\System\Webauthn\PluginTraits;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Document\HtmlDocument;
use Joomla\CMS\Helper\AuthenticationHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\User\UserHelper;
use Joomla\Event\Event;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Inserts Webauthn buttons into login modules
*
* @since 4.0.0
*/
trait AdditionalLoginButtons
{
/**
* Do I need to inject buttons? Automatically detected (i.e. disabled if I'm already logged
* in).
*
* @var boolean|null
* @since 4.0.0
*/
protected $allowButtonDisplay = null;
/**
* Have I already injected CSS and JavaScript? Prevents double inclusion of the same files.
*
* @var boolean
* @since 4.0.0
*/
private $injectedCSSandJS = false;
/**
* Creates additional login buttons
*
* @param Event $event The event we are handling
*
* @return void
*
* @see AuthenticationHelper::getLoginButtons()
*
* @since 4.0.0
*/
public function onUserLoginButtons(Event $event): void
{
/** @var string $form The HTML ID of the form we are enclosed in */
[$form] = array_values($event->getArguments());
// If we determined we should not inject a button return early
if (!$this->mustDisplayButton()) {
return;
}
// Load necessary CSS and Javascript files
$this->addLoginCSSAndJavascript();
// Unique ID for this button (allows display of multiple modules on the page)
$randomId = 'plg_system_webauthn-' .
UserHelper::genRandomPassword(12) . '-' . UserHelper::genRandomPassword(8);
// Get local path to image
$image = HTMLHelper::_('image', 'plg_system_webauthn/webauthn.svg', '', '', true, true);
// If you can't find the image then skip it
$image = $image ? JPATH_ROOT . substr($image, \strlen(Uri::root(true))) : '';
// Extract image if it exists
$image = file_exists($image) ? file_get_contents($image) : '';
$this->returnFromEvent($event, [
[
'label' => 'PLG_SYSTEM_WEBAUTHN_LOGIN_LABEL',
'tooltip' => 'PLG_SYSTEM_WEBAUTHN_LOGIN_DESC',
'id' => $randomId,
'data-webauthn-form' => $form,
'svg' => $image,
'class' => 'plg_system_webauthn_login_button',
],
]);
}
/**
* Should I allow this plugin to add a WebAuthn login button?
*
* @return boolean
*
* @since 4.0.0
*/
private function mustDisplayButton(): bool
{
// We must have a valid application
if (!($this->getApplication() instanceof CMSApplication)) {
return false;
}
// This plugin only applies to the frontend and administrator applications
if (!$this->getApplication()->isClient('site') && !$this->getApplication()->isClient('administrator')) {
return false;
}
// We must have a valid user
if (empty($this->getApplication()->getIdentity())) {
return false;
}
if (\is_null($this->allowButtonDisplay)) {
$this->allowButtonDisplay = false;
/**
* Do not add a WebAuthn login button if we are already logged in
*/
if (!$this->getApplication()->getIdentity()->guest) {
return false;
}
/**
* Only display a button on HTML output
*/
try {
$document = $this->getApplication()->getDocument();
} catch (\Exception $e) {
$document = null;
}
if (!($document instanceof HtmlDocument)) {
return false;
}
/**
* WebAuthn only works on HTTPS. This is a security-related limitation of the W3C Web Authentication
* specification, not an issue with this plugin :)
*/
if (!Uri::getInstance()->isSsl()) {
return false;
}
// All checks passed; we should allow displaying a WebAuthn login button
$this->allowButtonDisplay = true;
}
return $this->allowButtonDisplay;
}
/**
* Injects the WebAuthn CSS and Javascript for frontend logins, but only once per page load.
*
* @return void
*
* @since 4.0.0
*/
private function addLoginCSSAndJavascript(): void
{
if ($this->injectedCSSandJS) {
return;
}
// Set the "don't load again" flag
$this->injectedCSSandJS = true;
/** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->getApplication()->getDocument()->getWebAssetManager();
if (!$wa->assetExists('style', 'plg_system_webauthn.button')) {
$wa->registerStyle('plg_system_webauthn.button', 'plg_system_webauthn/button.css');
}
if (!$wa->assetExists('script', 'plg_system_webauthn.login')) {
$wa->registerScript('plg_system_webauthn.login', 'plg_system_webauthn/login.js', [], ['defer' => true], ['core']);
}
$wa->useStyle('plg_system_webauthn.button')
->useScript('plg_system_webauthn.login');
// Load language strings client-side
Text::script('PLG_SYSTEM_WEBAUTHN_ERR_CANNOT_FIND_USERNAME');
Text::script('PLG_SYSTEM_WEBAUTHN_ERR_EMPTY_USERNAME');
Text::script('PLG_SYSTEM_WEBAUTHN_ERR_INVALID_USERNAME');
// Store the current URL as the default return URL after login (or failure)
$this->getApplication()->getSession()->set('plg_system_webauthn.returnUrl', Uri::current());
}
}