Update to work with Keycloak 26

This commit is contained in:
Oliver Traber 2024-10-08 23:44:54 +02:00
parent 410a09aec6
commit e63d2dc74d
Signed by: Bluemedia
GPG key ID: C0674B105057136C
4 changed files with 388 additions and 198 deletions

View file

@ -72,4 +72,4 @@ To add new languages, the corresponding file must be added under `static/login/r
Special thanks to the following projects, without which the development of keycloak-modern-login would not be possible: Special thanks to the following projects, without which the development of keycloak-modern-login would not be possible:
- [Keycloakify](https://github.com/keycloakify/keycloakify), from which the [`ftl_object_to_js_code_declaring_an_object`](src/static/login/baselayout.ftl) function and [Keycloak context type definitions](src/types/context.ts) are derived. - [Keycloakify](https://github.com/keycloakify/keycloakify), from which the [FTL object to JSON](src/static/login/baselayout.ftl) function and [Keycloak context type definitions](src/types/context.ts) are derived.

View file

@ -21,131 +21,203 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
--> -->
<#function are_same_path path searchedPath>
<#if path?size != searchedPath?size> <#--
<#return false> Code derived from Keycloakify (https://github.com/keycloakify/keycloakify/blob/main/src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl)
</#if> -->
<#local i=0>
<#list path as property> <#function toJsDeclarationString object path>
<#local searchedProperty=searchedPath[i]> <#local isHash = -1>
<#if searchedProperty?is_string && searchedProperty == "*">
<#continue>
</#if>
<#if searchedProperty?is_string && !property?is_string>
<#return false>
</#if>
<#if searchedProperty?is_number && !property?is_number>
<#return false>
</#if>
<#if searchedProperty?string != property?string>
<#return false>
</#if>
<#local i+= 1>
</#list>
<#return true>
</#function>
<#function ftl_object_to_js_code_declaring_an_object pageId object path>
<#local isHash = "">
<#attempt> <#attempt>
<#local isHash = object?is_hash || object?is_hash_ex> <#local isHash = object?is_hash || object?is_hash_ex>
<#recover> <#recover>
<#return "ABORT: Can't evaluate if " + path?join(".") + " is hash"> <#return "ABORT: Can't evaluate if " + path?join(".") + " is a hash">
</#attempt> </#attempt>
<#if isHash> <#if isHash>
<#if path?size gt 10> <#if path?size gt 10>
<#return "ABORT: Too many recursive calls"> <#return "ABORT: Too many recursive calls, path: " + path?join(".")>
</#if> </#if>
<#local keys = -1>
<#local keys = "">
<#attempt> <#attempt>
<#local keys = object?keys> <#local keys = object?keys>
<#recover> <#recover>
<#return "ABORT: We can't list keys on this object"> <#return "ABORT: We can't list keys on object">
</#attempt> </#attempt>
<#local out_seq = []> <#local outSeq = []>
<#list keys as key> <#list keys as key>
<#if ["class","declaredConstructors","superclass","declaringClass" ]?seq_contains(key) > <#if ["class","declaredConstructors","superclass","declaringClass" ]?seq_contains(key) >
<#continue> <#continue>
</#if> </#if>
<#if <#if (
( areSamePath(path, ["url"]) &&
["loginUpdatePasswordUrl", "loginUpdateProfileUrl", "loginUsernameReminderUrl", "loginUpdateTotpUrl"]?seq_contains(key) && ["loginUpdatePasswordUrl", "loginUpdateProfileUrl", "loginUsernameReminderUrl", "loginUpdateTotpUrl"]?seq_contains(key)
are_same_path(path, ["url"])
) || ( ) || (
key == "updateProfileCtx" && key == "updateProfileCtx" &&
are_same_path(path, []) areSamePath(path, [])
) || ( ) || (
<#-- https://github.com/keycloakify/keycloakify/pull/65#issuecomment-991896344 (reports with saml-post-form.ftl) -->
<#-- https://github.com/keycloakify/keycloakify/issues/91#issue-1212319466 (reports with error.ftl and Kc18) -->
<#-- https://github.com/keycloakify/keycloakify/issues/109#issuecomment-1134610163 -->
<#-- https://github.com/keycloakify/keycloakify/issues/357 -->
<#-- https://github.com/keycloakify/keycloakify/discussions/406#discussioncomment-7514787 -->
key == "loginAction" && key == "loginAction" &&
are_same_path(path, ["url"]) && areSamePath(path, ["url"]) &&
["saml-post-form.ftl", "error.ftl", "info.ftl"]?seq_contains(pageId) && ["saml-post-form.ftl", "error.ftl", "info.ftl", "login-oauth-grant.ftl", "logout-confirm.ftl", "login-oauth2-device-verify-user-code.ftl"]?seq_contains(pageId) &&
!(auth?has_content && auth.showTryAnotherWayLink()) !(auth?has_content && auth.showTryAnotherWayLink())
) || (
<#-- https://github.com/keycloakify/keycloakify/issues/362 -->
["secretData", "value"]?seq_contains(key) &&
areSamePath(path, [ "totp", "otpCredentials", "*" ])
) || ( ) || (
["contextData", "idpConfig", "idp", "authenticationSession"]?seq_contains(key) && ["contextData", "idpConfig", "idp", "authenticationSession"]?seq_contains(key) &&
are_same_path(path, ["brokerContext"]) && areSamePath(path, ["brokerContext"]) &&
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId) ["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId)
) || ( ) || (
key == "identityProviderBrokerCtx" && key == "identityProviderBrokerCtx" &&
are_same_path(path, []) && areSamePath(path, []) &&
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId) ["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId)
) || ( ) || (
["masterAdminClient", "delegateForUpdate", "defaultRole"]?seq_contains(key) && ["masterAdminClient", "delegateForUpdate", "defaultRole", "smtpConfig"]?seq_contains(key) &&
are_same_path(path, ["realm"]) areSamePath(path, ["realm"])
) || ( ) || (
["password"]?seq_contains(key) && areSamePath(path, ["realm"]) &&
are_same_path(path, ["login"]) !["name", "displayName", "displayNameHtml", "internationalizationEnabled", "registrationEmailAsUsername" ]?seq_contains(key)
) || (
(
key == "realm" ||
key == "container"
) &&
isSubpath(path, ["applications", "applications"])
) || (
key == "delegateForUpdate" &&
areSamePath(path, ["user"])
) || (
<#-- Security audit forwarded by Garth (Gmail) -->
key == "saml.signing.private.key" &&
areSamePath(path, ["client", "attributes"])
) || (
<#-- See: https://github.com/keycloakify/keycloakify/issues/534 -->
key == "password" &&
areSamePath(path, ["login"])
) || (
<#-- Remove realmAttributes added by https://github.com/jcputney/keycloak-theme-additional-info-extension for peace of mind. -->
key == "realmAttributes" &&
areSamePath(path, [])
) || (
<#-- attributesByName adds a lot of noise to the output and is not needed, we already have profile.attributes -->
key == "attributesByName" &&
areSamePath(path, ["profile"])
) || (
<#-- We already have the attributes in profile speedup the rendering by filtering it out from the register object -->
(key == "attributes" || key == "attributesByName") &&
areSamePath(path, ["register"])
) || (
areSamePath(path, ["properties"]) &&
(
key?starts_with("kc") ||
key == "locales" ||
key == "import" ||
key == "parent" ||
key == "meta" ||
key == "stylesCommon" ||
key == "styles" ||
key == "accountResourceProvider"
)
) || (
key == "execution" &&
areSamePath(path, [])
) || (
key == "entity" &&
areSamePath(path, ["user"])
) || (
key == "attributes" &&
areSamePath(path, ["realm"])
) || (
areSamePath(path, ["realm"]) &&
![
"name",
"registrationEmailAsUsername",
"editUsernameAllowed",
"isInternationalizationEnabled",
"identityFederationEnabled",
"userManagedAccessAllowed"
]?seq_contains(key)
) || (
["flowContext", "session", "realm"]?seq_contains(key) &&
areSamePath(path, ["social"])
) )
> >
<#-- <#local outSeq += ["/*" + path?join(".") + "." + key + " excluded*/"]> -->
<#continue> <#continue>
</#if> </#if>
<#if key == "attemptedUsername" && are_same_path(path, ["auth"])> <#-- https://github.com/keycloakify/keycloakify/discussions/406 -->
<#if (
key == "attemptedUsername" &&
areSamePath(path, ["auth"]) &&
[
"register.ftl", "terms.ftl", "info.ftl", "login.ftl",
"login-update-password.ftl", "login-oauth2-device-verify-user-code.ftl"
]?seq_contains(pageId)
)>
<#attempt> <#attempt>
<#-- https://github.com/keycloak/keycloak/blob/3a2bf0c04bcde185e497aaa32d0bb7ab7520cf4a/themes/src/main/resources/theme/base/login/template.ftl#L63 -->
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())> <#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
<#local outSeq += ["/*" + path?join(".") + "." + key + " excluded*/"]>
<#continue> <#continue>
</#if> </#if>
<#recover> <#recover>
<#local outSeq += ["/*Accessing attemptedUsername throwed an exception */"]>
</#attempt> </#attempt>
</#if> </#if>
{{userDefinedExclusions}}
<#attempt> <#attempt>
<#if !object[key]??> <#if !object[key]??>
<#continue> <#continue>
</#if> </#if>
<#recover> <#recover>
<#local out_seq += ["/*Couldn't test if '" + key + "' is available on this object*/"]> <#local outSeq += ["/*Couldn't test if '" + key + "' is available on this object*/"]>
<#continue> <#continue>
</#attempt> </#attempt>
<#local propertyValue = ""> <#local propertyValue = -1>
<#attempt> <#attempt>
<#local propertyValue = object[key]> <#local propertyValue = object[key]>
<#recover> <#recover>
<#local out_seq += ["/*Couldn't dereference '" + key + "' on this object*/"]> <#local outSeq += ["/*Couldn't dereference '" + key + "' on this object*/"]>
<#continue> <#continue>
</#attempt> </#attempt>
<#local rec_out = ftl_object_to_js_code_declaring_an_object(pageId, propertyValue, path + [ key ])> <#local recOut = toJsDeclarationString(propertyValue, path + [ key ])>
<#if rec_out?starts_with("ABORT:")>
<#local errorMessage = rec_out?remove_beginning("ABORT:")> <#if recOut?starts_with("ABORT:")>
<#local errorMessage = recOut?remove_beginning("ABORT:")>
<#if errorMessage != " It's a method" > <#if errorMessage != " It's a method" >
<#local out_seq += ["/*" + key + ": " + errorMessage + "*/"]> <#local outSeq += ["/*" + key + ": " + errorMessage + "*/"]>
</#if> </#if>
<#continue> <#continue>
</#if> </#if>
<#local out_seq += ['"' + key + '": ' + rec_out + ","]> <#local outSeq += ['"' + key + '": ' + recOut + ","]>
</#list> </#list>
<#return (["{"] + out_seq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "}"])?join("\n")> <#return (["{"] + outSeq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "}"])?join("\n")>
</#if> </#if>
<#local isMethod = ""> <#local isMethod = -1>
<#attempt> <#attempt>
<#local isMethod = object?is_method> <#local isMethod = object?is_method>
<#recover> <#recover>
@ -153,7 +225,8 @@ SOFTWARE.
</#attempt> </#attempt>
<#if isMethod> <#if isMethod>
<#if are_same_path(path, ["auth", "showUsername"])>
<#if areSamePath(path, ["auth", "showUsername"])>
<#attempt> <#attempt>
<#return auth.showUsername()?c> <#return auth.showUsername()?c>
<#recover> <#recover>
@ -161,7 +234,7 @@ SOFTWARE.
</#attempt> </#attempt>
</#if> </#if>
<#if are_same_path(path, ["auth", "showResetCredentials"])> <#if areSamePath(path, ["auth", "showResetCredentials"])>
<#attempt> <#attempt>
<#return auth.showResetCredentials()?c> <#return auth.showResetCredentials()?c>
<#recover> <#recover>
@ -169,7 +242,7 @@ SOFTWARE.
</#attempt> </#attempt>
</#if> </#if>
<#if are_same_path(path, ["auth", "showTryAnotherWayLink"])> <#if areSamePath(path, ["auth", "showTryAnotherWayLink"])>
<#attempt> <#attempt>
<#return auth.showTryAnotherWayLink()?c> <#return auth.showTryAnotherWayLink()?c>
<#recover> <#recover>
@ -177,10 +250,41 @@ SOFTWARE.
</#attempt> </#attempt>
</#if> </#if>
<#if areSamePath(path, ["url", "getLogoutUrl"])>
<#local returnValue = -1>
<#attempt>
<#local returnValue = url.getLogoutUrl()>
<#recover>
<#return "ABORT: Couldn't evaluate url.getLogoutUrl()">
</#attempt>
<#return 'function(){ return "' + returnValue + '"; }'>
</#if>
<#if areSamePath(path, ["totp", "policy", "getAlgorithmKey"])>
<#local returnValue = "error">
<#if mode?? && mode = "manual">
<#attempt>
<#local returnValue = totp.policy.getAlgorithmKey()>
<#recover>
<#return "ABORT: Couldn't evaluate totp.policy.getAlgorithmKey()">
</#attempt>
</#if>
<#return 'function(){ return "' + returnValue + '"; }'>
</#if>
<#if profile?? && profile.attributes??>
<#list profile.attributes as attribute>
<#if fieldNames?seq_contains(attribute.name)>
<#continue>
</#if>
<#assign fieldNames += [attribute.name]>
</#list>
</#if>
<#return "ABORT: It's a method"> <#return "ABORT: It's a method">
</#if> </#if>
<#local isBoolean = ""> <#local isBoolean = -1>
<#attempt> <#attempt>
<#local isBoolean = object?is_boolean> <#local isBoolean = object?is_boolean>
<#recover> <#recover>
@ -191,46 +295,137 @@ SOFTWARE.
<#return object?c> <#return object?c>
</#if> </#if>
<#local isEnumerable = ""> <#local isEnumerable = -1>
<#attempt> <#attempt>
<#local isEnumerable = object?is_enumerable> <#local isEnumerable = object?is_enumerable>
<#recover> <#recover>
<#return "ABORT: Can't test if it's an enumerable"> <#return "ABORT: Can't test if it's an enumerable">
</#attempt> </#attempt>
<#if isEnumerable> <#if isEnumerable>
<#local out_seq = []>
<#local outSeq = []>
<#local i = 0> <#local i = 0>
<#list object as array_item> <#list object as array_item>
<#if !array_item??> <#if !array_item??>
<#local out_seq += ["null,"]> <#local outSeq += ["null,"]>
<#continue> <#continue>
</#if> </#if>
<#local rec_out = ftl_object_to_js_code_declaring_an_object(pageId, array_item, path + [ i ])> <#local recOut = toJsDeclarationString(array_item, path + [ i ])>
<#local i = i + 1> <#local i = i + 1>
<#if rec_out?starts_with("ABORT:")> <#if recOut?starts_with("ABORT:")>
<#local errorMessage = rec_out?remove_beginning("ABORT:")>
<#local errorMessage = recOut?remove_beginning("ABORT:")>
<#if errorMessage != " It's a method" > <#if errorMessage != " It's a method" >
<#local out_seq += ["/*" + i?string + ": " + errorMessage + "*/"]> <#local outSeq += ["/*" + i?string + ": " + errorMessage + "*/"]>
</#if> </#if>
<#continue> <#continue>
</#if> </#if>
<#local out_seq += [rec_out + ","]>
<#local outSeq += [recOut + ","]>
</#list> </#list>
<#return (["["] + out_seq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "]"])?join("\n")> <#return (["["] + outSeq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "]"])?join("\n")>
</#if> </#if>
<#local isDate = -1>
<#attempt>
<#local isDate = object?is_date_like>
<#recover>
<#return "ABORT: Can't test if it's a date">
</#attempt>
<#if isDate>
<#return '"' + object?datetime?iso_utc + '"'>
</#if>
<#local isNumber = -1>
<#attempt>
<#local isNumber = object?is_number>
<#recover>
<#return "ABORT: Can't test if it's a number">
</#attempt>
<#if isNumber>
<#return object?c>
</#if>
<#local isString = -1>
<#attempt>
<#local isString = object?is_string>
<#recover>
<#return "ABORT: Can't test if it's a string">
</#attempt>
<#attempt> <#attempt>
<#return '"' + object?js_string + '"'>; <#return '"' + object?js_string + '"'>;
<#recover> <#recover>
</#attempt> </#attempt>
<#return "ABORT: Couldn't convert into string non hash, non method, non boolean, non enumerable object"> <#return "ABORT: Couldn't convert into string non hash, non method, non boolean, non number, non enumerable object">
</#function>
<#function isSubpath path searchedPath>
<#if path?size < searchedPath?size>
<#return false>
</#if>
<#local i=0>
<#list path as property>
<#if i == searchedPath?size >
<#continue>
</#if>
<#local searchedProperty=searchedPath[i]>
<#local i+= 1>
<#if searchedProperty?is_string && searchedProperty == "*">
<#continue>
</#if>
<#if searchedProperty?is_string && !property?is_string>
<#return false>
</#if>
<#if searchedProperty?is_number && !property?is_number>
<#return false>
</#if>
<#if searchedProperty?string != property?string>
<#return false>
</#if>
</#list>
<#return true>
</#function>
<#function areSamePath path searchedPath>
<#return path?size == searchedPath?size && isSubpath(path, searchedPath)>
</#function>
<#function removeBrackets str>
<#if str?starts_with("${") && str?ends_with("}")>
<#return str[2..(str?length-2)]>
<#else>
<#return str>
</#if>
</#function> </#function>
<#macro baseLayout pageId="template.ftl"> <#macro baseLayout pageId="template.ftl">
@ -252,7 +447,7 @@ SOFTWARE.
} }
</style> </style>
<script> <script>
window.kcContext = ${ftl_object_to_js_code_declaring_an_object(pageId, .data_model, [])?no_esc} window.kcContext = ${toJsDeclarationString(.data_model, [])?no_esc};
</script> </script>
<#nested "head"> <#nested "head">
</head> </head>

View file

@ -1,2 +1,2 @@
locales=en locales=en
parent=keycloak parent=keycloak.v2

View file

@ -3304,11 +3304,6 @@ minimist@^1.2.0, minimist@^1.2.6:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
moment@^2.29.4:
version "2.29.4"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
ms@2.1.2: ms@2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"