Skip to content

Commit

Permalink
feat(jans-auth-server): added new HealthCheck custom sсript (#8144)
Browse files Browse the repository at this point in the history
* feat(jans-auth-server): added definition of new HealthCheck custom script #7991

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): added external health check service

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): corrected different typos

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): injected health check custom script into controller

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): added sample health check custom script

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* doc(jans-auth-server): added documentation for health check custom script

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

---------

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>
  • Loading branch information
yuriyz committed Mar 28, 2024
1 parent 0848eee commit 5e299e9
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 6 deletions.
1 change: 1 addition & 0 deletions docs/admin/developer/interception-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/admin/developer/scripts/create-user.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
98 changes: 98 additions & 0 deletions docs/admin/developer/scripts/health-check.md
Original file line number Diff line number Diff line change
@@ -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<String, SimpleCustomProperty> configurationAttributes) {
scriptLogger.info("Initialized HealthCheck Java custom script.");
return true;
}

@Override
public boolean init(CustomScript customScript, Map<String, SimpleCustomProperty> configurationAttributes) {
scriptLogger.info("Initialized HealthCheck Java custom script.");
return true;
}

@Override
public boolean destroy(Map<String, SimpleCustomProperty> 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)
54 changes: 54 additions & 0 deletions docs/script-catalog/health_check/HealthCheck.java
Original file line number Diff line number Diff line change
@@ -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<String, SimpleCustomProperty> configurationAttributes) {
scriptLogger.info("Initialized HealthCheck Java custom script.");
return true;
}

@Override
public boolean init(CustomScript customScript, Map<String, SimpleCustomProperty> configurationAttributes) {
scriptLogger.info("Initialized HealthCheck Java custom script.");
return true;
}

@Override
public boolean destroy(Map<String, SimpleCustomProperty> configurationAttributes) {
scriptLogger.info("Destroyed HealthCheck Java custom script.");
return true;
}

@Override
public int getApiVersion() {
return 11;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ 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);

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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<CustomScriptConfiguration> 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<CustomScriptConfiguration> getScriptsToExecute() {
Set<CustomScriptConfiguration> result = Sets.newHashSet();
if (this.customScriptConfigurations == null) {
return result;
}

result.addAll(customScriptConfigurations);
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()),
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, SimpleCustomProperty> configurationAttributes) {
return true;
}

@Override
public boolean init(CustomScript customScript, Map<String, SimpleCustomProperty> configurationAttributes) {
return true;
}

@Override
public boolean destroy(Map<String, SimpleCustomProperty> configurationAttributes) {
return true;
}

@Override
public int getApiVersion() {
return 11;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 5e299e9

Please sign in to comment.