Skip to content
Snippets Groups Projects
Verified Commit f85ec6e3 authored by Pavel Břoušek's avatar Pavel Břoušek
Browse files

feat: template for privacyidea

parent a34fd7f8
No related branches found
No related tags found
No related merge requests found
...@@ -66,3 +66,9 @@ msgstr "Přihlášení uživatelským jménem a heslem" ...@@ -66,3 +66,9 @@ msgstr "Přihlášení uživatelským jménem a heslem"
msgid "{campusmultiauth:localLogin_capslock}" msgid "{campusmultiauth:localLogin_capslock}"
msgstr "Pozor, máte zapnutý Caps lock." msgstr "Pozor, máte zapnutý Caps lock."
msgid "{campusmultiauth:close}"
msgstr "zavřít"
msgid "{campusmultiauth:otp_help}"
msgstr "Vložte jednorázový kód, například z TOTP aplikace."
...@@ -66,3 +66,9 @@ msgstr "Login with username and password" ...@@ -66,3 +66,9 @@ msgstr "Login with username and password"
msgid "{campusmultiauth:localLogin_capslock}" msgid "{campusmultiauth:localLogin_capslock}"
msgstr "Warning! Caps lock is ON." msgstr "Warning! Caps lock is ON."
msgid "{campusmultiauth:close}"
msgstr "close"
msgid "{campusmultiauth:otp_help}"
msgstr "Enter a one time code, e.g. from a TOTP app."
<?php declare(strict_types=1);
use SimpleSAML\Module;
$this->data['header'] = $this->t('{privacyidea:privacyidea:login_title_challenge}');
$this->data['head'] .= '<link rel="stylesheet" href="'
. htmlspecialchars(Module::getModuleUrl('privacyidea/css/loginform.css'), ENT_QUOTES)
. '" media="screen" />';
$this->data['head'] .= '<link rel="stylesheet" href="'
. htmlspecialchars(Module::getModuleUrl('campusmultiauth/resources/privacyidea.css'), ENT_QUOTES)
. '" media="screen" />';
$this->includeAtTemplateBase('includes/header.php');
// Prepare error case to show it in UI if needed
if ($this->data['errorCode'] !== null) {
?>
<div class="message message--common message--common-error" role="alert">
<a href="#" class="message__close icon icon-times" title="<?php echo $this->t('{campusmultiauth:close}'); ?>">
<span class="vhide"><?php echo $this->t('{campusmultiauth:close}'); ?></span>
</a>
<span class="message__icon icon icon-exclamation-triangle"></span>
<h2 class="message__title"><?php echo $this->t('{login:error_header}'); ?></h2>
<p class="message__desc">
<?php
echo htmlspecialchars(
sprintf('%s%s: %s', $this->t(
'{privacyidea:privacyidea:error}'
), $this->data['errorCode'] ? (' ' . $this->data['errorCode']) : '', $this->data['errorMessage'])
); ?>
</p>
</div>
<?php
} // end of errorcode
?>
<div class="container">
<div class="login">
<form action="FormReceiver.php" method="POST" id="piLoginForm" name="piLoginForm" class="loginForm">
<div class="form-panel first valid" id="gaia_firstform">
<div class="slide-out ">
<div class="input-wrapper focused">
<div class="identifier-shown grid">
<?php if ($this->data['webauthnAvailable']) { ?>
<div class="grid__cell size--m--2-4">
<h2><?php echo $this->t('{privacyidea:privacyidea:webauthn}'); ?></h2>
<p id="message" role="alert"><?php
$messageOverride = $this->data['messageOverride'] ?? null;
if ($messageOverride === null || is_string($messageOverride)) {
echo htmlspecialchars(
$messageOverride ?? $this->data['message'] ?? '',
ENT_QUOTES
);
} elseif (is_callable($messageOverride)) {
echo call_user_func($messageOverride, $this->data['message'] ?? '');
}
?></p>
<p>
<button id="useWebAuthnButton" name="useWebAuthnButton" class="btn btn-primary btn-s" type="button">
<span><?php echo $this->t('{privacyidea:privacyidea:webauthn}'); ?></span>
</button>
</p>
</div>
<?php } ?>
<?php if ($this->data['otpAvailable'] ?? true) { ?>
<div class="grid__cell size--m--2-4">
<h2><?php echo $this->t('{privacyidea:privacyidea:otp}'); ?></h2>
<p><?php echo $this->t('{campusmultiauth:otp_help}'); ?></p>
<div class="form-inline">
<p class="size--m--4-4 size--l--6-12">
<label for="otp" class="sr-only"><?php echo $this->t('{privacyidea:privacyidea:otp}'); ?></label>
<span class="inp-fix">
<input id="otp" name="otp" tabindex="1" value="" class="text inp-text" autocomplete="one-time-code" type="number" inputmode="numeric" pattern="[0-9]{6,}" required placeholder="<?php echo htmlspecialchars($otpHint, ENT_QUOTES); ?>"<?php if ($this->data['noAlternatives']) {
echo ' autofocus';
} ?> />
</span>
</p>
<p>
<button id="submitButton" tabindex="1" class="rc-button rc-button-submit btn btn-primary btn-s nowrap" type="submit" name="Submit">
<span><?php echo htmlspecialchars($this->t('{login:login_button}'), ENT_QUOTES); ?></span>
</button>
</p>
</div>
</div>
<?php } ?>
<!-- Undefined index is suppressed and the default is used for these values -->
<input id="mode" type="hidden" name="mode" value="otp"
data-preferred="<?php echo htmlspecialchars($this->data['mode'], ENT_QUOTES); ?>"/>
<input id="pushAvailable" type="hidden" name="pushAvailable"
value="<?php echo ($this->data['pushAvailable'] ?? false) ? 'true' : ''; ?>"/>
<input id="otpAvailable" type="hidden" name="otpAvailable"
value="<?php echo ($this->data['otpAvailable'] ?? true) ? 'true' : ''; ?>"/>
<input id="webAuthnSignRequest" type="hidden" name="webAuthnSignRequest"
value='<?php echo htmlspecialchars($this->data['webAuthnSignRequest'] ?? '', ENT_QUOTES); ?>'/>
<input id="u2fSignRequest" type="hidden" name="u2fSignRequest"
value='<?php echo htmlspecialchars($this->data['u2fSignRequest'] ?? '', ENT_QUOTES); ?>'/>
<input id="modeChanged" type="hidden" name="modeChanged" value=""/>
<input id="step" type="hidden" name="step"
value="<?php echo htmlspecialchars(strval(($this->data['step'] ?? null) ?: 2), ENT_QUOTES); ?>"/>
<input id="webAuthnSignResponse" type="hidden" name="webAuthnSignResponse" value=""/>
<input id="u2fSignResponse" type="hidden" name="u2fSignResponse" value=""/>
<input id="origin" type="hidden" name="origin" value=""/>
<input id="loadCounter" type="hidden" name="loadCounter"
value="<?php echo htmlspecialchars(strval(($this->data['loadCounter'] ?? null) ?: 1), ENT_QUOTES); ?>"/>
<!-- Additional input to persist the message -->
<input type="hidden" name="message"
value="<?php echo htmlspecialchars($this->data['message'] ?? '', ENT_QUOTES); ?>"/>
<?php
// If enrollToken load QR Code
if (isset($this->data['tokenQR'])) {
echo htmlspecialchars($this->t('{privacyidea:privacyidea:scan_token_qr}')); ?>
<div class="tokenQR">
<?php echo '<img src="' . $this->data['tokenQR'] . '" />'; ?>
</div>
<?php
}
?>
</div>
<?php
// Organizations
if (array_key_exists('organizations', $this->data)) {
?>
<div class="identifier-shown">
<label for="organization"><?php echo htmlspecialchars($this->t('{login:organization}')); ?></label>
<select id="organization" name="organization" tabindex="3">
<?php
if (array_key_exists('selectedOrg', $this->data)) {
$selectedOrg = $this->data['selectedOrg'];
} else {
$selectedOrg = null;
}
foreach ($this->data['organizations'] as $orgId => $orgDesc) {
if (is_array($orgDesc)) {
$orgDesc = $this->t($orgDesc);
}
if ($orgId === $selectedOrg) {
$selected = 'selected="selected" ';
} else {
$selected = '';
}
echo '<option ' . $selected . 'value="' . htmlspecialchars(
$orgId,
ENT_QUOTES
) . '">' . htmlspecialchars($orgDesc) . '</option>';
} ?>
</select>
</div>
<?php
} ?>
</div> <!-- focused -->
</div> <!-- slide-out-->
</div> <!-- form-panel -->
</form>
<?php
// Logout
if (($this->data['showLogout'] ?? true) && isset($this->data['LogoutURL'])) { ?>
<p>
<a href="<?php echo htmlspecialchars($this->data['LogoutURL']); ?>"><?php echo $this->t('{status:logout}'); ?></a>
</p>
<?php } ?>
</div> <!-- End of login -->
</div> <!-- End of container -->
<?php
if (!empty($this->data['links'])) {
echo '<ul class="links">';
foreach ($this->data['links'] as $l) {
echo '<li><a href="' . htmlspecialchars($l['href'], ENT_QUOTES) . '">' . htmlspecialchars(
$this->t($l['text'])
) . '</a></li>';
}
echo '</ul>';
}
?>
<script src="<?php echo htmlspecialchars(Module::getModuleUrl('privacyidea/js/pi-webauthn.js'), ENT_QUOTES); ?>">
</script>
<script src="<?php echo htmlspecialchars(Module::getModuleUrl('privacyidea/js/u2f-api.js'), ENT_QUOTES); ?>">
</script>
<meta id="privacyidea-step" name="privacyidea-step" content="<?php echo $this->data['step']; ?>">
<meta id="privacyidea-translations" name="privacyidea-translations" content="<?php echo htmlspecialchars(json_encode($this->data['translations'])); ?>">
<script src="<?php echo htmlspecialchars(Module::getModuleUrl('privacyidea/js/loginform.js'), ENT_QUOTES); ?>">
</script>
<script src="<?php echo htmlspecialchars(Module::getModuleUrl('campusmultiauth/resources/privacyidea.js'), ENT_QUOTES); ?>">
</script>
<?php
$this->includeAtTemplateBase('includes/footer.php');
?>
{% set pagetitle = '{privacyidea:privacyidea:login_title_challenge}'|trans %}
{% extends "base.twig" %}
{% block preload %}
<link rel="stylesheet" href="/{{ baseurlpath }}module.php/privacyidea/css/loginform.css" media="screen">
<link rel="stylesheet" href="/{{ baseurlpath }}module.php/campusmultiauth/resources/privacyidea.css" media="screen">
{% endblock %}
{% block content %}
{% if errorCode %}
<div class="message message--common message--common-error" role="alert">
<a href="#" class="message__close icon icon-times" title="{{ '{campusmultiauth:close}'|trans }}">
<span class="vhide">{{ '{campusmultiauth:close}'|trans }}</span>
</a>
<span class="message__icon icon icon-exclamation-triangle"></span>
<h2 class="message__title">{{ '{login:error_header}'|trans }}</h2>
<p class="message__desc">
{{ '{privacyidea:privacyidea:error}'|trans }}{% if errorCode %} {{ errorCode }}{% endif %}: {{ errorMessage }}
</p>
</div>
{% endif %}
<div class="container">
<div class="login">
<form action="FormReceiver.php" method="POST" id="piLoginForm" name="piLoginForm" class="loginForm">
<div class="form-panel first valid" id="gaia_firstform">
<div class="slide-out">
<div class="input-wrapper focused">
<div class="identifier-shown grid">
{% if webauthnAvailable %}
<div class="grid__cell size--m--2-4">
<h2>{{ '{privacyidea:privacyidea:webauthn}'|trans }}</h2>
<p id="message" role="alert">
{{ messageOverride }}
</p>
<p>
<button id="useWebAuthnButton" name="useWebAuthnButton" class="btn btn-primary btn-s" type="button">
<span>{{ '{privacyidea:privacyidea:webauthn}'|trans }}</span>
</button>
</p>
</div>
{% endif %}
{% if otpAvailable %}
<div class="grid__cell size--m--2-4">
<h2>{{ '{privacyidea:privacyidea:otp}'|trans }}</h2>
<p>{{ '{campusmultiauth:otp_help}'|trans }}</p>
<div class="form-inline">
<p class="size--m--4-4 size--l--6-12">
<label for="otp" class="sr-only">{{ '{privacyidea:privacyidea:otp}'|trans }}</label>
<span class="inp-fix">
<input id="otp" name="otp" tabindex="1" value="" class="text inp-text" autocomplete="one-time-code" type="number" inputmode="numeric" pattern="[0-9]{6,}" required placeholder="{{ otpHint }}"{% if otpAvailable is defined and otpAvailable and noAlternatives %} autofocus{% endif %} />
</span>
</p>
<p>
<button id="submitButton" tabindex="1" class="rc-button rc-button-submit btn btn-primary btn-s nowrap" type="submit" name="Submit">
<span>{{ '{login:login_button}'|trans }}</span>
</button>
</p>
</div>
</div>
{% endif %}
{# Undefined index is suppressed and the default is used for these values #}
<input id="mode" type="hidden" name="mode" value="otp" data-preferred="{{ mode }}"/>
<input id="pushAvailable" type="hidden" name="pushAvailable" value="{% if pushAvailable %}true{% endif %}"/>
<input id="otpAvailable" type="hidden" name="otpAvailable" value="{% if otpAvailable %}true{% endif %}"/>
<input id="webAuthnSignRequest" type="hidden" name="webAuthnSignRequest"
value='{% if webAuthnSignRequest is defined %}{{ webAuthnSignRequest }}{% endif %}'/>
<input id="u2fSignRequest" type="hidden" name="u2fSignRequest"
value='{% if u2fSignRequest is defined %}{{ u2fSignRequest }}{% endif %}'/>
<input id="modeChanged" type="hidden" name="modeChanged" value=""/>
<input id="step" type="hidden" name="step"
value="{% if step is defined and step %}{{ step }}{% else %}2{% endif %}"/>
<input id="webAuthnSignResponse" type="hidden" name="webAuthnSignResponse" value=""/>
<input id="u2fSignResponse" type="hidden" name="u2fSignResponse" value=""/>
<input id="origin" type="hidden" name="origin" value=""/>
<input id="loadCounter" type="hidden" name="loadCounter"
value="{% if loadCounter is defined and loadCounter %}{{ loadCounter }}{% else %}1{% endif %}"/>
{# Additional input to persist the message #}
<input type="hidden" name="message"
value="{% if message is defined %}{{ message }}{% endif %}"/>
{# If enrollToken load QR Code #}
{% if tokenQR is defined %}
{{ '{privacyidea:privacyidea:scan_token_qr}'|trans }}
<div class="tokenQR">
<img src="{{ tokenQR }}" />
</div>
{% endif %}
</div>
{# Organizations #}
{% if organizations is defined %}
<div class="identifier-shown">
<label for="organization">{{ '{login:organization}'|trans }}</label>
<select id="organization" name="organization" tabindex="3">
{% for orgId, orgDesc in organizations %}
<option {% if selectedOrg is defined and orgId == selectedOrg %}selected {% endif %}value="{{ orgId }}">
{{ orgDesc|trans }}
</option>
{% endfor %}
</select>
</div>
{% endif %}
</div> <!-- focused -->
</div> <!-- slide-out-->
</div> <!-- form-panel -->
</form>
{# Logout #}
{% if showLogout is defined and showLogout and LogoutURL is defined %}
<p>
<a href="{{ LogoutURL }}">{{ '{status:logout}'|trans }}</a>
</p>
{% endif %}
</div> <!-- End of login -->
</div> <!-- End of container -->
{% if links is defined and links %}
<ul class="links">
{% for l in links %}
<li><a href="{{ l['href'] }}">{{ l['text']|trans }}</a></li>
{% endfor %}
</ul>
{% endif %}
<script src="/{{ baseurlpath }}module.php/privacyidea/js/pi-webauthn.js"></script>
<script src="/{{ baseurlpath }}module.php/privacyidea/js/u2f-api.js"></script>
<meta id="privacyidea-step" name="privacyidea-step" content="{{ step }}">
<meta id="privacyidea-translations" name="privacyidea-translations" content="{{ translations | json_encode }}">
<script src="/{{ baseurlpath }}module.php/privacyidea/js/loginform.js"></script>
<script src="/{{ baseurlpath }}module.php/campusmultiauth/resources/privacyidea.js"></script>
{% endblock %}
html #useWebAuthnButton,
html #usePushButton,
html #useOTPButton,
html #useU2FButton,
html #submitButton {
margin: 0;
width: auto;
}
html #otp {
margin: 0;
width: 100%;
}
const closeMessage = (e) => {
e.target.parentElement.classList.add('hide');
e.preventDefault();
return false;
};
document.addEventListener('DOMContentLoaded', () => {
// close buttons
document.querySelectorAll('.message__close').forEach((closeButton) => {
closeButton.addEventListener('click', closeMessage);
});
// allow WebAuthn and OTP on one page
['otp', 'submitButton'].forEach((id) => {
const el = document.getElementById(id);
if (el) {
el.classList.remove('hidden');
}
});
const piLoginForm = document.getElementById('piLoginForm');
if (piLoginForm) {
piLoginForm.addEventListener('submit', () => {
document.getElementById('mode').value = 'otp';
});
}
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment