From 19e744027e797d41a625688e506a1b1be0e24eac Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Mon, 25 Mar 2024 16:15:37 +0200 Subject: [PATCH 1/6] feat(jans-auth-server): added definition of new HealthCheck custom script #7991 Signed-off-by: YuriyZ --- .../model/custom/script/CustomScriptType.java | 3 ++ .../script/type/health/DummyHealthCheck.java | 37 +++++++++++++++++++ .../script/type/health/HealthCheckType.java | 15 ++++++++ 3 files changed, 55 insertions(+) create mode 100644 jans-core/script/src/main/java/io/jans/model/custom/script/type/health/DummyHealthCheck.java create mode 100644 jans-core/script/src/main/java/io/jans/model/custom/script/type/health/HealthCheckType.java diff --git a/jans-core/script/src/main/java/io/jans/model/custom/script/CustomScriptType.java b/jans-core/script/src/main/java/io/jans/model/custom/script/CustomScriptType.java index da935f37dc0..1511d70333e 100644 --- a/jans-core/script/src/main/java/io/jans/model/custom/script/CustomScriptType.java +++ b/jans-core/script/src/main/java/io/jans/model/custom/script/CustomScriptType.java @@ -29,6 +29,8 @@ import io.jans.model.custom.script.type.discovery.DummyDiscoveryType; import io.jans.model.custom.script.type.fido2.DummyFido2ExtensionType; import io.jans.model.custom.script.type.fido2.Fido2ExtensionType; +import io.jans.model.custom.script.type.health.DummyHealthCheck; +import io.jans.model.custom.script.type.health.HealthCheckType; import io.jans.model.custom.script.type.id.DummyIdGeneratorType; import io.jans.model.custom.script.type.id.IdGeneratorType; import io.jans.model.custom.script.type.idp.DummyIdpType; @@ -110,6 +112,7 @@ public enum CustomScriptType implements AttributeEnum { PERSISTENCE_EXTENSION("persistence_extension", "Persistence Extension", PersistenceType.class, CustomScript.class, "PersistenceExtension", new DummyPeristenceType()), IDP("idp", "Idp Extension", IdpType.class, CustomScript.class, "IdpExtension", new DummyIdpType()), DISCOVERY("discovery", "Discovery", DiscoveryType.class, CustomScript.class, "Discovery", new DummyDiscoveryType()), + HEALTH_CHECK("health_check", "Health Check", HealthCheckType.class, CustomScript.class, "HealthCheck", new DummyHealthCheck()), AUTHZ_DETAIL("authz_detail", "Authorization Detail", AuthzDetailType.class, CustomScript.class, "AuthzDetail", new DummyAuthzDetail()), UPDATE_TOKEN("update_token", "Update Token", UpdateTokenType.class, CustomScript.class, "UpdateToken", new DummyUpdateTokenType()), CONFIG_API("config_api_auth", "Config Api Auth", ConfigApiType.class, CustomScript.class,"ConfigApiAuthorization", new DummyConfigApiType()), diff --git a/jans-core/script/src/main/java/io/jans/model/custom/script/type/health/DummyHealthCheck.java b/jans-core/script/src/main/java/io/jans/model/custom/script/type/health/DummyHealthCheck.java new file mode 100644 index 00000000000..c3ca4527c0f --- /dev/null +++ b/jans-core/script/src/main/java/io/jans/model/custom/script/type/health/DummyHealthCheck.java @@ -0,0 +1,37 @@ +package io.jans.model.custom.script.type.health; + +import io.jans.model.SimpleCustomProperty; +import io.jans.model.custom.script.model.CustomScript; + +import java.util.Map; + +/** + * @author Yuriy Z + */ +public class DummyHealthCheck implements HealthCheckType { + + @Override + public String healthCheck(Object context) { + return null; + } + + @Override + public boolean init(Map configurationAttributes) { + return true; + } + + @Override + public boolean init(CustomScript customScript, Map configurationAttributes) { + return true; + } + + @Override + public boolean destroy(Map configurationAttributes) { + return true; + } + + @Override + public int getApiVersion() { + return 11; + } +} diff --git a/jans-core/script/src/main/java/io/jans/model/custom/script/type/health/HealthCheckType.java b/jans-core/script/src/main/java/io/jans/model/custom/script/type/health/HealthCheckType.java new file mode 100644 index 00000000000..a542ef27fa8 --- /dev/null +++ b/jans-core/script/src/main/java/io/jans/model/custom/script/type/health/HealthCheckType.java @@ -0,0 +1,15 @@ +package io.jans.model.custom.script.type.health; + +import io.jans.model.custom.script.type.BaseExternalType; + +/** + * @author Yuriy Z + */ +public interface HealthCheckType extends BaseExternalType { + + /** + * @param context script context + * @return health check response + */ + String healthCheck(Object context); +} From 580cad3dda53515a731adf64bc9c16407ddb6d29 Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Tue, 26 Mar 2024 14:46:23 +0200 Subject: [PATCH 2/6] feat(jans-auth-server): added external health check service Signed-off-by: YuriyZ --- .../external/ExternalHealthCheckService.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalHealthCheckService.java diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalHealthCheckService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalHealthCheckService.java new file mode 100644 index 00000000000..9ec22bb93db --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalHealthCheckService.java @@ -0,0 +1,76 @@ +package io.jans.as.server.service.external; + +import com.google.common.collect.Sets; +import io.jans.as.server.model.common.ExecutionContext; +import io.jans.as.server.service.external.context.ExternalScriptContext; +import io.jans.model.custom.script.CustomScriptType; +import io.jans.model.custom.script.conf.CustomScriptConfiguration; +import io.jans.model.custom.script.type.health.HealthCheckType; +import io.jans.service.custom.script.ExternalScriptService; +import jakarta.ejb.DependsOn; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Named; +import org.apache.commons.lang3.StringUtils; + +import java.util.Set; + +/** + * @author Yuriy Z + */ +@ApplicationScoped +@DependsOn("appInitializer") +@Named +public class ExternalHealthCheckService extends ExternalScriptService { + + public ExternalHealthCheckService() { + super(CustomScriptType.HEALTH_CHECK); + } + + public String externalHealthCheck(ExecutionContext context) { + final Set scripts = getScriptsToExecute(); + if (scripts.isEmpty()) { + return null; + } + + log.trace("Found {} health-check scripts.", scripts.size()); + + for (CustomScriptConfiguration script : scripts) { + final String result = externalHealthCheck(script, context); + if (StringUtils.isNotBlank(result)) { + log.debug("'healthCheck' returned result {}, script: {}", result, script.getName()); + return result; + } + } + + return null; + } + + private String externalHealthCheck(CustomScriptConfiguration scriptConfiguration, ExecutionContext context) { + try { + log.trace("Executing external 'healthCheck' method, script name: {}, context: {}", scriptConfiguration.getName(), context); + + HealthCheckType script = (HealthCheckType) scriptConfiguration.getExternalType(); + context.setScript(scriptConfiguration); + + final ExternalScriptContext scriptContext = new ExternalScriptContext(context); + final String result = script.healthCheck(scriptContext); + + log.trace("Finished external 'healthCheck' method, script name: {}, context: {}, result: {}", scriptConfiguration.getName(), context, result); + return result; + } catch (Exception ex) { + log.error(ex.getMessage(), ex); + saveScriptError(scriptConfiguration.getCustomScript(), ex); + return null; + } + } + + private Set getScriptsToExecute() { + Set result = Sets.newHashSet(); + if (this.customScriptConfigurations == null) { + return result; + } + + result.addAll(customScriptConfigurations); + return result; + } +} From c7237c703edb52c09b9f92092e347f014cc28e6b Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Wed, 27 Mar 2024 14:30:45 +0200 Subject: [PATCH 3/6] feat(jans-auth-server): corrected different typos Signed-off-by: YuriyZ --- docs/admin/developer/scripts/create-user.md | 2 +- .../as/server/service/external/ExternalCreateUserService.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/admin/developer/scripts/create-user.md b/docs/admin/developer/scripts/create-user.md index b2c750ea3cb..9a7e69f9f09 100644 --- a/docs/admin/developer/scripts/create-user.md +++ b/docs/admin/developer/scripts/create-user.md @@ -8,7 +8,7 @@ tags: # Create User Custom Script ## Overview -"CreateUser" custom script is use when Authorization Endpoint is called with `prompt=create` parameter. +"CreateUser" custom script is used when Authorization Endpoint is called with `prompt=create` parameter. In this case AS shows user registration form instead of authn/authz UI. "CreateUser" custom script allows inject/modify user registration process. diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalCreateUserService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalCreateUserService.java index 1f86a0faa62..480f54797af 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalCreateUserService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalCreateUserService.java @@ -47,7 +47,7 @@ public boolean externalCreate(ExecutionContext context) { public boolean externalCreate(CustomScriptConfiguration scriptConfiguration, ExecutionContext context) { try { - log.trace("Executing external 'externalCreateUser' method, script name: {}, context: {}", scriptConfiguration.getName(), context); + log.trace("Executing external 'createUser' method, script name: {}, context: {}", scriptConfiguration.getName(), context); CreateUserType script = (CreateUserType) scriptConfiguration.getExternalType(); context.setScript(scriptConfiguration); @@ -55,7 +55,7 @@ public boolean externalCreate(CustomScriptConfiguration scriptConfiguration, Exe final ExternalScriptContext scriptContext = new ExternalScriptContext(context); final boolean result = script.createUser(scriptContext); - log.trace("Finished external 'externalCreateUser' method, script name: {}, context: {}, result: {}", scriptConfiguration.getName(), context, result); + log.trace("Finished external 'createUser' method, script name: {}, context: {}, result: {}", scriptConfiguration.getName(), context, result); return result; } catch (Exception ex) { log.error(ex.getMessage(), ex); From 0ae66f177362bf1ebc389096d2fa48245e959da2 Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Wed, 27 Mar 2024 14:31:25 +0200 Subject: [PATCH 4/6] feat(jans-auth-server): injected health check custom script into controller Signed-off-by: YuriyZ --- .../rs/controller/HealthCheckController.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/ws/rs/controller/HealthCheckController.java b/jans-auth-server/server/src/main/java/io/jans/as/server/ws/rs/controller/HealthCheckController.java index a673961672b..9d665f3df1d 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/ws/rs/controller/HealthCheckController.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ws/rs/controller/HealthCheckController.java @@ -8,17 +8,22 @@ import io.jans.as.model.common.FeatureFlagType; import io.jans.as.model.error.ErrorResponseFactory; +import io.jans.as.server.model.common.ExecutionContext; import io.jans.as.server.service.external.ExternalAuthenticationService; import io.jans.as.server.service.external.ExternalDynamicScopeService; +import io.jans.as.server.service.external.ExternalHealthCheckService; import io.jans.orm.PersistenceEntryManager; - import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.MediaType; +import org.apache.commons.lang3.StringUtils; /** * Health check controller @@ -42,16 +47,30 @@ public class HealthCheckController { @Inject private ErrorResponseFactory errorResponseFactory; + @Inject + private ExternalHealthCheckService externalHealthCheckService; + @GET @POST @Path("/health-check") @Produces(MediaType.APPLICATION_JSON) - public String healthCheckController() { + public String healthCheckController(@Context HttpServletRequest httpRequest, @Context HttpServletResponse httpResponse) { errorResponseFactory.validateFeatureEnabled(FeatureFlagType.HEALTH_CHECK); boolean isConnected = persistenceEntryManager.getOperationService().isConnected(); String dbStatus = isConnected ? "online" : "offline"; String appStatus = getAppStatus(); - return "{\"status\": \"" + appStatus + "\", \"db_status\":\"" + dbStatus + "\"}"; + final String responseString = String.format("{\"status\": \"%s\", \"db_status\":\"%s\"}", appStatus, dbStatus); + + if (externalHealthCheckService.isEnabled()) { + final ExecutionContext executionContext = new ExecutionContext(httpRequest, httpResponse); + final String externalResponse = externalHealthCheckService.externalHealthCheck(executionContext); + if (StringUtils.isNotBlank(externalResponse)) { + return externalResponse; + } + + } + + return responseString; } public String getAppStatus() { From d544b2db5fe7324af26c115abf8db5d9bc3d3896 Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Thu, 28 Mar 2024 12:26:23 +0200 Subject: [PATCH 5/6] feat(jans-auth-server): added sample health check custom script Signed-off-by: YuriyZ --- .../health_check/HealthCheck.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 docs/script-catalog/health_check/HealthCheck.java diff --git a/docs/script-catalog/health_check/HealthCheck.java b/docs/script-catalog/health_check/HealthCheck.java new file mode 100644 index 00000000000..b0cf483cc75 --- /dev/null +++ b/docs/script-catalog/health_check/HealthCheck.java @@ -0,0 +1,54 @@ +package io.jans.as.server._scripts; + +import io.jans.as.server.service.external.context.ExternalScriptContext; +import io.jans.model.SimpleCustomProperty; +import io.jans.model.custom.script.model.CustomScript; +import io.jans.model.custom.script.type.health.HealthCheckType; +import io.jans.service.custom.script.CustomScriptManager; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +/** + * @author Yuriy Z + */ +public class HealthCheck implements HealthCheckType { + + private static final Logger log = LoggerFactory.getLogger(HealthCheck.class); + private static final Logger scriptLogger = LoggerFactory.getLogger(CustomScriptManager.class); + + @Override + public String healthCheck(Object context) { + ExternalScriptContext scriptContext = (ExternalScriptContext) context; + HttpServletRequest httpRequest = scriptContext.getExecutionContext().getHttpRequest(); + + String appStatus = "running"; + String dbStatus = "online"; + return String.format("{\"status\": \"%s\", \"db_status\":\"%s\"}", appStatus, dbStatus); + } + + @Override + public boolean init(Map configurationAttributes) { + scriptLogger.info("Initialized HealthCheck Java custom script."); + return true; + } + + @Override + public boolean init(CustomScript customScript, Map configurationAttributes) { + scriptLogger.info("Initialized HealthCheck Java custom script."); + return true; + } + + @Override + public boolean destroy(Map configurationAttributes) { + scriptLogger.info("Destroyed HealthCheck Java custom script."); + return true; + } + + @Override + public int getApiVersion() { + return 11; + } +} From 281241a34b851e7d18104e686175cd71ef3a2949 Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Thu, 28 Mar 2024 12:27:13 +0200 Subject: [PATCH 6/6] doc(jans-auth-server): added documentation for health check custom script Signed-off-by: YuriyZ --- docs/admin/developer/interception-scripts.md | 1 + docs/admin/developer/scripts/health-check.md | 98 ++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 docs/admin/developer/scripts/health-check.md diff --git a/docs/admin/developer/interception-scripts.md b/docs/admin/developer/interception-scripts.md index 773c67d4f25..1bdd6fe4cbd 100644 --- a/docs/admin/developer/interception-scripts.md +++ b/docs/admin/developer/interception-scripts.md @@ -40,6 +40,7 @@ calling external APIs 1. [Authz Detail](./scripts/authz-detail.md) 1. [Create User](./scripts/create-user.md) 1. [Select Account](./scripts/select-account.md) +1. [Health Check](./scripts/health-check.md) 1. Resource Owner Password Credentials 1. UMA 2 RPT Authorization Policies 1. UMA 2 Claims-Gathering diff --git a/docs/admin/developer/scripts/health-check.md b/docs/admin/developer/scripts/health-check.md new file mode 100644 index 00000000000..9a5d2f0c251 --- /dev/null +++ b/docs/admin/developer/scripts/health-check.md @@ -0,0 +1,98 @@ +--- +tags: + - administration + - developer + - scripts +--- + +# Create User Custom Script + +## Overview +"HealthCheck" custom script is used when Health Check Endpoint is called. +Custom script allows inject/modify response from Health Check Endpoint. + +## Interface +The HealthCheck script implements the [HealthCheckType](https://github.com/JanssenProject/jans/blob/main/jans-core/script/src/main/java/io/jans/model/custom/script/type/health_check/HealthCheckType.java) interface. This extends methods from the base script type in addition to adding new methods: + +### Inherited Methods +| Method header | Method description | +|:-----|:------| +| `def init(self, customScript, configurationAttributes)` | This method is only called once during the script initialization. It can be used for global script initialization, initiate objects etc | +| `def destroy(self, configurationAttributes)` | This method is called once to destroy events. It can be used to free resource and objects created in the `init()` method | +| `def getApiVersion(self, configurationAttributes, customScript)` | The getApiVersion method allows API changes in order to do transparent migration from an old script to a new API. Only include the customScript variable if the value for getApiVersion is greater than 10 | + +### New methods +| Method header | Method description | +|:-----|:------| +|`def healthCheck(self, context)`| Returns response for health check endpoint + + +### Objects +| Object name | Object description | +|:-----|:------| +|`customScript`| The custom script object. [Reference](https://github.com/JanssenProject/jans/blob/main/jans-core/script/src/main/java/io/jans/model/custom/script/model/CustomScript.java) | +|`context`| [Reference](https://github.com/JanssenProject/jans/blob/main/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ExternalScriptContext.java) | + + +### Saample Scrip in Java + +```java +package io.jans.as.server._scripts; + +import io.jans.as.server.service.external.context.ExternalScriptContext; +import io.jans.model.SimpleCustomProperty; +import io.jans.model.custom.script.model.CustomScript; +import io.jans.model.custom.script.type.health.HealthCheckType; +import io.jans.service.custom.script.CustomScriptManager; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +/** + * @author Yuriy Z + */ +public class HealthCheck implements HealthCheckType { + + private static final Logger log = LoggerFactory.getLogger(HealthCheck.class); + private static final Logger scriptLogger = LoggerFactory.getLogger(CustomScriptManager.class); + + @Override + public String healthCheck(Object context) { + ExternalScriptContext scriptContext = (ExternalScriptContext) context; + HttpServletRequest httpRequest = scriptContext.getExecutionContext().getHttpRequest(); + + String appStatus = "running"; + String dbStatus = "online"; + return String.format("{\"status\": \"%s\", \"db_status\":\"%s\"}", appStatus, dbStatus); + } + + @Override + public boolean init(Map configurationAttributes) { + scriptLogger.info("Initialized HealthCheck Java custom script."); + return true; + } + + @Override + public boolean init(CustomScript customScript, Map configurationAttributes) { + scriptLogger.info("Initialized HealthCheck Java custom script."); + return true; + } + + @Override + public boolean destroy(Map configurationAttributes) { + scriptLogger.info("Destroyed HealthCheck Java custom script."); + return true; + } + + @Override + public int getApiVersion() { + return 11; + } +} + +``` + +## Sample Scripts +- [Sample HealthCheck script](../../../script-catalog/health_check/HealthCheck.java)