Skip to content

Commit

Permalink
feat: added sovity theme to keycloak (#264)
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilczaja authored Aug 12, 2024
1 parent 0306ec9 commit 4dc10b5
Show file tree
Hide file tree
Showing 36 changed files with 4,564 additions and 45 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "505910bc-ea1d-497e-8107-461505d89736",
"realm": "mds-portal",
"displayName": "MDS Portal",
"realm": "authority-portal",
"displayName": "Authority Portal",
"notBefore": 0,
"defaultSignatureAlgorithm": "RS256",
"revokeRefreshToken": false,
Expand Down Expand Up @@ -49,7 +49,7 @@
{
"id": "d24a6aa3-9805-4324-bc12-51ee5d794156",
"name": "UR_AUTHORITY-PORTAL_AUTHORITY-ADMIN",
"description": "Admin of Dataspace Authority (MDS).",
"description": "Admin of Dataspace Authority.",
"composite": true,
"composites": {
"realm": [
Expand All @@ -63,7 +63,7 @@
{
"id": "04b696ff-910f-4de8-849c-5d1f886546a4",
"name": "UR_AUTHORITY-PORTAL_AUTHORITY-USER",
"description": "User of Dataspace Authority (MDS).",
"description": "User of Dataspace Authority.",
"composite": false,
"clientRole": false,
"containerId": "505910bc-ea1d-497e-8107-461505d89736",
Expand Down Expand Up @@ -627,13 +627,13 @@
"clientId": "account",
"name": "${client_account}",
"rootUrl": "${authBaseUrl}",
"baseUrl": "/realms/mds-portal/account/",
"baseUrl": "/realms/authority-portal/account/",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"redirectUris": [
"/realms/mds-portal/account/*"
"/realms/authority-portal/account/*"
],
"webOrigins": [],
"notBefore": 0,
Expand Down Expand Up @@ -671,13 +671,13 @@
"clientId": "account-console",
"name": "${client_account-console}",
"rootUrl": "${authBaseUrl}",
"baseUrl": "/realms/mds-portal/account/",
"baseUrl": "/realms/authority-portal/account/",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"redirectUris": [
"/realms/mds-portal/account/*"
"/realms/authority-portal/account/*"
],
"webOrigins": [],
"notBefore": 0,
Expand Down Expand Up @@ -1029,13 +1029,13 @@
"clientId": "security-admin-console",
"name": "${client_security-admin-console}",
"rootUrl": "${authAdminUrl}",
"baseUrl": "/admin/mds-portal/console/",
"baseUrl": "/admin/authority-portal/console/",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"redirectUris": [
"/admin/mds-portal/console/*"
"/admin/authority-portal/console/*"
],
"webOrigins": [
"+"
Expand Down Expand Up @@ -1623,15 +1623,15 @@
"smtpServer": {
"host": "keycloak-mailserver",
"from": "[email protected]",
"fromDisplayName": "MDS Portal",
"fromDisplayName": "Authority Portal",
"starttls": "false",
"port": "1025",
"ssl": "false"
},
"loginTheme": "mds-theme",
"loginTheme": "sovity-theme",
"accountTheme": "",
"adminTheme": "",
"emailTheme": "mds-theme",
"emailTheme": "sovity-theme",
"eventsEnabled": false,
"eventsListeners": [
"jboss-logging"
Expand Down
2 changes: 1 addition & 1 deletion authority-portal-backend/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ java = "17"
kotlin = "1.9.22"
npmVersion = "8.15.0"

sovity-edcCe = "10.0.0"
sovity-edcCe = "10.1.0"

quarkus = "3.9.2"
quarkus-keycloakAdminClientReactive = "3.6.6"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export interface AppConfigEnv {
AUTHORITY_PORTAL_FRONTEND_USE_FAKE_BACKEND: string;
AUTHORITY_PORTAL_FRONTEND_USE_LOCAL_BACKEND: string;
AUTHORITY_PORTAL_FRONTEND_INVALIDATE_SESSION_COOKIES_URL: string;
AUTHORITY_PORTAL_FRONTEND_IFRAME_URL: string;
AUTHORITY_PORTAL_FRONTEND_IFRAME_URL?: string;
AUTHORITY_PORTAL_FRONTEND_PRIVACY_POLICY_URL: string;
AUTHORITY_PORTAL_FRONTEND_LEGAL_NOTICE_URL: string;
AUTHORITY_PORTAL_FRONTEND_SUPPORT_URL: string;
Expand Down Expand Up @@ -103,7 +103,7 @@ export function buildAppConfig(envVars: AppConfigEnv): AppConfig {
useLocalBackend:
envVars.AUTHORITY_PORTAL_FRONTEND_USE_LOCAL_BACKEND === 'true',

iframeUrl: envVars.AUTHORITY_PORTAL_FRONTEND_IFRAME_URL,
iframeUrl: envVars.AUTHORITY_PORTAL_FRONTEND_IFRAME_URL ?? '',
privacyPolicyUrl: envVars.AUTHORITY_PORTAL_FRONTEND_PRIVACY_POLICY_URL,
legalNoticeUrl: envVars.AUTHORITY_PORTAL_FRONTEND_LEGAL_NOTICE_URL,
supportUrl: envVars.AUTHORITY_PORTAL_FRONTEND_SUPPORT_URL,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<#import "template.ftl" as layout>
<@layout.emailLayout>
${kcSanitize(msg("emailVerificationBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration), user.getFirstName(), user.getLastName()))?no_esc}
</@layout.emailLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<#outputformat "plainText">
<#assign requiredActionsText><#if requiredActions??><#list requiredActions><#items as reqActionItem>${msg("requiredAction.${reqActionItem}")}<#sep>, </#sep></#items></#list></#if></#assign>
</#outputformat>

<#import "template.ftl" as layout>
<@layout.emailLayout>
${kcSanitize(msg("executeActionsBodyHtml",link, linkExpiration, realmName, requiredActionsText, linkExpirationFormatter(linkExpiration), user.getFirstName(), user.getLastName()))?no_esc}
</@layout.emailLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<#import "template.ftl" as layout>
<@layout.emailLayout>
${kcSanitize(msg("passwordResetBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration)))?no_esc}
</@layout.emailLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<#macro emailLayout>
<html>
<body>
<#nested>
</body>
</html>
</#macro>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
executeActionsSubject=Authority Portal - Finalize your registration
executeActionsBody=Dear {5} {6},\n\nWe are pleased to invite you to join our community!\n\nIn the Authority Portal you will find all information and functionalities concerning your data requests and offers. It is your personal members area and your direct and fast way into our dataspace.\n\nSimply click on the link below and you are in!\n\n{0} (expires within {4})
executeActionsBodyHtml=Dear {5} {6},<br/><br/>We are pleased to invite you to join our community in the Authority Portal!<br/><br/>In the Authority Portal you will find all information and functionalities concerning your data requests and offers. It is your personal members area and your direct and fast way into our dataspace.<br/><br/>Simply click on the link below and you are in!<br/><br/><a href="{0}">{0}</a> (expires within {4})

emailVerificationSubject=Authority Portal - Verify your email address
emailVerificationBody=Hi {4} {5},\nAn account has been created with this email address.\nIf this was you, click the link below to verify your email address.\n\n{0}\n\nThis link will expire within {3}.\n\nIf you did not create this account and are not aware of having been invited, feel free to ignore this message.
emailVerificationBodyHtml=Hi {4} {5},<br/><br/>An account has been created with this email address.<br/>If this was you, click the link below to verify your email address.<br/><br/><a href="{0}">{0}</a><br/><br/>This link will expire within {3}.<br/><br/>If you did not create this account and are not aware of having been invited, feel free to ignore this message.

passwordResetSubject=Authority Portal - Reset password
passwordResetBody=A password reset has been requested for your {2} account. If this was you, click on the link below to reset your password.\n\n{0}\n\nThis link and code will expire within {3}.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
passwordResetBodyHtml=<p>A password reset has been requested for your {2} account. If this was you, click on the link below to reset your password.</p><p><a href="{0}">Reset your password</a></p><p>This link will expire within {3}.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>

requiredAction.CONFIGURE_TOTP=Configure OTP
requiredAction.TERMS_AND_CONDITIONS=Accept Terms and Conditions
requiredAction.UPDATE_PASSWORD=Update Password
requiredAction.UPDATE_PROFILE=Update Profile
requiredAction.VERIFY_EMAIL=Verify Email
requiredAction.CONFIGURE_RECOVERY_AUTHN_CODES=Generate Recovery Codes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<#ftl output_format="plainText">
${msg("emailVerificationBody",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration), user.getFirstName(), user.getLastName())}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<#ftl output_format="plainText">
<#assign requiredActionsText><#if requiredActions??><#list requiredActions><#items as reqActionItem>${msg("requiredAction.${reqActionItem}")}<#sep>, </#items></#list><#else></#if></#assign>

${msg("executeActionsBody",link, linkExpiration, realmName, requiredActionsText, linkExpirationFormatter(linkExpiration), user.getFirstName(), user.getLastName())}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
parent=base
108 changes: 108 additions & 0 deletions authority-portal-keycloak/sovity-theme/login/login-config-totp.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<#import "template.ftl" as layout>
<@layout.registrationLayout displayRequiredFields=false displayMessage=!messagesPerField.existsError('totp','userLabel'); section>

<#if section = "header">

<#elseif section = "form">
<ol id="kc-totp-settings">
<li>
<p>${msg("loginTotpStep1")}</p>

<ul id="kc-totp-supported-apps">
<#list totp.supportedApplications as app>
<li>${msg(app)}</li>
</#list>
</ul>
</li>

<#if mode?? && mode = "manual">
<li>
<p>${msg("loginTotpManualStep2")}</p>
<p><span id="kc-totp-secret-key">${totp.totpSecretEncoded}</span></p>
<p><a href="${totp.qrUrl}" id="mode-barcode">${msg("loginTotpScanBarcode")}</a></p>
</li>
<li>
<p>${msg("loginTotpManualStep3")}</p>
<p>
<ul>
<li id="kc-totp-type">${msg("loginTotpType")}: ${msg("loginTotp." + totp.policy.type)}</li>
<li id="kc-totp-algorithm">${msg("loginTotpAlgorithm")}: ${totp.policy.getAlgorithmKey()}</li>
<li id="kc-totp-digits">${msg("loginTotpDigits")}: ${totp.policy.digits}</li>
<#if totp.policy.type = "totp">
<li id="kc-totp-period">${msg("loginTotpInterval")}: ${totp.policy.period}</li>
<#elseif totp.policy.type = "hotp">
<li id="kc-totp-counter">${msg("loginTotpCounter")}: ${totp.policy.initialCounter}</li>
</#if>
</ul>
</p>
</li>
<#else>
<li>
<p>${msg("loginTotpStep2")}</p>
<img id="kc-totp-secret-qr-code" src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"><br/>
<p><a href="${totp.manualUrl}" id="mode-manual">${msg("loginTotpUnableToScan")}</a></p>
</li>
</#if>
<li>
<p>${msg("loginTotpStep3")}</p>
<p>${msg("loginTotpStep3DeviceName")}</p>
</li>
</ol>

<form action="${url.loginAction}" class="${properties.kcFormClass!} formContainer" id="kc-totp-settings-form" method="post">
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcInputWrapperClass!}">
<label for="totp" class="control-label">${msg("authenticatorCode")}</label> <span class="required">*</span>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" id="totp" name="totp" autocomplete="off" class="${properties.kcInputClass!}"
aria-invalid="<#if messagesPerField.existsError('totp')>true</#if>"
/>

<#if messagesPerField.existsError('totp')>
<span id="input-error-otp-code" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('totp'))?no_esc}
</span>
</#if>

</div>
<input type="hidden" id="totpSecret" name="totpSecret" value="${totp.totpSecret}" />
<#if mode??><input type="hidden" id="mode" name="mode" value="${mode}"/></#if>
</div>

<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcInputWrapperClass!}">
<label for="userLabel" class="control-label">${msg("loginTotpDeviceName")}</label> <#if totp.otpCredentials?size gte 1><span class="required">*</span></#if>
</div>

<div class="${properties.kcInputWrapperClass!}">
<input type="text" class="${properties.kcInputClass!}" id="userLabel" name="userLabel" autocomplete="off"
aria-invalid="<#if messagesPerField.existsError('userLabel')>true</#if>"
/>

<#if messagesPerField.existsError('userLabel')>
<span id="input-error-otp-label" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('userLabel'))?no_esc}
</span>
</#if>
</div>
</div>

<#if isAppInitiatedAction??>
<input type="submit"
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}"
id="saveTOTPBtn" value="${msg("doSubmit")}"
/>
<button type="submit"
class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!} ${properties.kcButtonLargeClass!}"
id="cancelTOTPBtn" name="cancel-aia" value="true" />${msg("doCancel")}
</button>
<#else>
<input type="submit"
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
id="saveTOTPBtn" value="${msg("doSubmit")}"
/>
</#if>
</form>
</#if>
</@layout.registrationLayout>
58 changes: 58 additions & 0 deletions authority-portal-keycloak/sovity-theme/login/login-otp.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<#import "template.ftl" as layout>
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('totp'); section>
<#if section="header">
${msg("doLogIn")}
<#elseif section="form">
<form id="kc-otp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}"
method="post">
<#if otpLogin.userOtpCredentials?size gt 1>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcInputWrapperClass!}">
<#list otpLogin.userOtpCredentials as otpCredential>
<input id="kc-otp-credential-${otpCredential?index}" class="${properties.kcLoginOTPListInputClass!}" type="radio" name="selectedCredentialId" value="${otpCredential.id}" <#if otpCredential.id == otpLogin.selectedCredentialId>checked="checked"</#if>>
<label for="kc-otp-credential-${otpCredential?index}" class="${properties.kcLoginOTPListClass!}" tabindex="${otpCredential?index}">
<span class="${properties.kcLoginOTPListItemHeaderClass!}">
<span class="${properties.kcLoginOTPListItemIconBodyClass!}">
<i class="${properties.kcLoginOTPListItemIconClass!}" aria-hidden="true"></i>
</span>
<span class="${properties.kcLoginOTPListItemTitleClass!}">${otpCredential.userLabel}</span>
</span>
</label>
</#list>
</div>
</div>
</#if>

<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="otp" class="${properties.kcLabelClass!} otp-label">${msg("loginOtpOneTime")}</label>
</div>

<div class="${properties.kcInputWrapperClass!}">
<input id="otp" name="otp" autocomplete="off" type="text" class="text-input"
autofocus aria-invalid="<#if messagesPerField.existsError('totp')>true</#if>"/>

<#if messagesPerField.existsError('totp')>
<span id="input-error-otp-code" class="${properties.kcInputErrorMessageClass!}"
aria-live="polite">
${kcSanitize(messagesPerField.get('totp'))?no_esc}
</span>
</#if>
</div>
</div>

<div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}">
</div>
</div>

<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<input
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}" />
</div>
</div>
</form>
</#if>
</@layout.registrationLayout>
Loading

0 comments on commit 4dc10b5

Please sign in to comment.