Skip to content

Commit

Permalink
feat(jans-auth-server): introduced `/.well-known/authzen-configuratio…
Browse files Browse the repository at this point in the history
…n` endpoint (#10321)

* feat(jans-auth-server): introduced /.well-known/authzen-configuration

Signed-off-by: YuriyZ <[email protected]>

* feat(jans-auth-server): added external service for /.well-known/authzen-configuration

Signed-off-by: YuriyZ <[email protected]>

* feat(jans-auth-server): added custom script definition for /.well-known/authzen-configuration

Signed-off-by: YuriyZ <[email protected]>

* feat(jans-auth-server): added cache support for /.well-known/authzen-configuration

Signed-off-by: YuriyZ <[email protected]>

* feat(jans-auth-server): added sample custom script for /.well-known/authzen-configuration

Signed-off-by: YuriyZ <[email protected]>

* feat(jans-auth-server): added authzen ws classes to initializers

Signed-off-by: YuriyZ <[email protected]>

* feat(jans-auth-server): added proxypass in apache for /.well-known/authzen-configuration

Signed-off-by: YuriyZ <[email protected]>

* feat(jans-auth-server): updated swagger with /.well-known/authzen-configuration

Signed-off-by: YuriyZ <[email protected]>

* feat(jans-auth-server): added documentation for authzen discovery custom script

Signed-off-by: YuriyZ <[email protected]>

* feat(jans-auth-server): added documentation about authzen discovery

Signed-off-by: YuriyZ <[email protected]>

---------

Signed-off-by: YuriyZ <[email protected]>
  • Loading branch information
yuriyz authored Dec 6, 2024
1 parent ecd8e38 commit efb7ab6
Show file tree
Hide file tree
Showing 19 changed files with 501 additions and 5 deletions.
25 changes: 23 additions & 2 deletions docs/janssen-server/auth-server/endpoints/access-evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,34 @@ The endpoint's responses are typically concise, aiming to provide a rapid decisi
The goal is to provide a scalable, secure interface for dynamic and fine-grained access control across applications.


URL to access access evaluation endpoint on Janssen Server is listed in the response of Janssen Server's well-known
[configuration endpoint](./configuration.md) given below.
URL to access access evaluation endpoint on Janssen Server is listed in both:
- the response of Janssen Server's well-known [configuration endpoint](./configuration.md) given below.
- the response of Janssen Server's `/.well-known/authzen-configuration` endpoint.

**OpenID Discovery**
```text
https://janssen.server.host/jans-auth/.well-known/openid-configuration
```

**AuthZEN Discovery**
```text
https://janssen.server.host/jans-auth/.well-known/authzen-configuration
```

`/.well-known/authzen-configuration` allows to publish data specific to AuthZEN only. Response of AuthZEN discovery endpoint can be
changed via `AccessEvaluationDiscoveryType` custom script.

**Snippet of AccessEvaluationDiscoveryType**
```java
@Override
public boolean modifyResponse(Object responseAsJsonObject, Object context) {
scriptLogger.info("write to script logger");
JSONObject response = (JSONObject) responseAsJsonObject;
response.accumulate("key_from_java", "value_from_script_on_java");
return true;
}
```

`access_evaluation_v1_endpoint` claim in the response specifies the URL for access evaluation endpoint. By default, access
evaluation endpoint looks like below:

Expand Down
1 change: 1 addition & 0 deletions docs/janssen-server/developer/interception-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ calling external APIs
1. [Post Authentication](./scripts/post-authentication.md)
1. [Authorization Challenge](./scripts/authorization-challenge.md)
1. [Access Evaluation](./scripts/access-evaluation.md)
1. [Access Evaluation Discovery](./scripts/access-evaluation-discovery.md)
1. [Authz Detail](./scripts/authz-detail.md)
1. [Create User](./scripts/create-user.md)
1. [Select Account](./scripts/select-account.md)
Expand Down
1 change: 1 addition & 0 deletions docs/janssen-server/developer/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ overridden to implement your business case.
| [Post Authentication](../../../script-catalog/post_authn/post-authentication.md) | |
| [Authorization Challenge](../../../script-catalog/authorization_challenge/authorization-challenge.md) | |
| [Access Evaluation](../../../script-catalog/access_evaluation/access-evaluation.md) | Access Evaluation custom script for Access Evaluation Endpoint (AuthZEN) |
| [Access Evaluation Discovery](../../../script-catalog/access_evaluation/access-evaluation-discovery.md) | Access Evaluation Discovery custom script for `/.well-known/authzen-configuration` Access Evaluation Discovery Endpoint (AuthZEN) |
| [Select Account](../../../script-catalog/select_account/select-account.md) | |
| [Resource Owner Password Credentials](../../../script-catalog/resource_owner_password_credentials/ropc.md) | |
| [UMA 2 RPT Authorization Policies](../../../script-catalog/uma_rpt_policy/uma-rpt.md) | |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* Copyright (c) 2024, Gluu
Author: Yuriy Z
*/

import io.jans.model.SimpleCustomProperty;
import io.jans.model.custom.script.model.CustomScript;
import io.jans.model.custom.script.type.authzen.AccessEvaluationDiscoveryType;
import io.jans.service.custom.script.CustomScriptManager;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

public class AccessEvaluationDiscovery implements AccessEvaluationDiscoveryType {

private static final Logger log = LoggerFactory.getLogger(AccessEvaluationDiscovery.class);
private static final Logger scriptLogger = LoggerFactory.getLogger(CustomScriptManager.class);

@Override
public boolean init(Map<String, SimpleCustomProperty> configurationAttributes) {
log.info("Init of AccessEvaluationDiscovery Java custom script");
return true;
}

@Override
public boolean init(CustomScript customScript, Map<String, SimpleCustomProperty> configurationAttributes) {
log.info("Init of AccessEvaluationDiscovery Java custom script");
return true;
}

@Override
public boolean destroy(Map<String, SimpleCustomProperty> configurationAttributes) {
log.info("Destroy of AccessEvaluationDiscovery Java custom script");
return true;
}

@Override
public int getApiVersion() {
log.info("getApiVersion AccessEvaluationDiscovery Java custom script: 11");
return 11;
}

@Override
public boolean modifyResponse(Object responseAsJsonObject, Object context) {
scriptLogger.info("write to script logger");
JSONObject response = (JSONObject) responseAsJsonObject;
response.accumulate("key_from_java", "value_from_script_on_java");
return true;
}
}

108 changes: 108 additions & 0 deletions docs/script-catalog/access_evaluation/access-evaluation-discovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
tags:
- administration
- developer
- script-catalog
---

# Access Evaluation Discovery Custom Script

## Overview

This script is used to control Access Evaluation Discovery Endpoint (`/.well-known/authzen-configuration`).

**Sample request**
```
GET /.well-known/authzen-configuration HTTP/1.1
Host: happy-example.gluu.info
Content-Type: application/json
{"access_evaluation_v1_endpoint":"https://happy-example.gluu.info/jans-auth/restv1/evaluation"}
```



## Interface
The Access Evaluation Discovery script implements the [AccessEvaluationDiscoveryType](https://github.com/JanssenProject/jans/blob/main/jans-core/script/src/main/java/io/jans/model/custom/script/type/authzen/AccessEvaluationDiscoveryType.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 modifyResponse(self, responseAsJsonObject, context)` | This method is called after discovery response is ready. This method can modify discovery response.<br/>`responseAsJsonObject` is `org.codehaus.jettison.json.JSONObject`<br/> `context` is `io.jans.as.server.model.common.ExecutionContext` |


`modifyResponse` method returns `true` to access modification or `false` to revert all changes.


### 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) |


## Sample Demo Custom Script

### Script Type: Java

```java
import io.jans.model.SimpleCustomProperty;
import io.jans.model.custom.script.model.CustomScript;
import io.jans.model.custom.script.type.authzen.AccessEvaluationDiscoveryType;
import io.jans.service.custom.script.CustomScriptManager;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

public class AccessEvaluationDiscovery implements AccessEvaluationDiscoveryType {

private static final Logger log = LoggerFactory.getLogger(AccessEvaluationDiscovery.class);
private static final Logger scriptLogger = LoggerFactory.getLogger(CustomScriptManager.class);

@Override
public boolean init(Map<String, SimpleCustomProperty> configurationAttributes) {
log.info("Init of AccessEvaluationDiscovery Java custom script");
return true;
}

@Override
public boolean init(CustomScript customScript, Map<String, SimpleCustomProperty> configurationAttributes) {
log.info("Init of AccessEvaluationDiscovery Java custom script");
return true;
}

@Override
public boolean destroy(Map<String, SimpleCustomProperty> configurationAttributes) {
log.info("Destroy of AccessEvaluationDiscovery Java custom script");
return true;
}

@Override
public int getApiVersion() {
log.info("getApiVersion AccessEvaluationDiscovery Java custom script: 11");
return 11;
}

@Override
public boolean modifyResponse(Object responseAsJsonObject, Object context) {
scriptLogger.info("write to script logger");
JSONObject response = (JSONObject) responseAsJsonObject;
response.accumulate("key_from_java", "value_from_script_on_java");
return true;
}
}
```


## Sample Scripts
- [Access Evaluation Discovery](../../../script-catalog/access_evaluation/AccessEvaluationDiscovery.java)
46 changes: 46 additions & 0 deletions jans-auth-server/docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,47 @@ tags:
description: Janssen Authorization Server is an open source OpenID Connect Provider (OP) and UMA Authorization Server (AS). The project also includes OpenID Connect Client code which can be used by websites to validate tokens. Server currently implements all required aspects of the OpenID Connect stack, including an OAuth 2.0 authorization server, Simple Web Discovery, Dynamic Client Registration, JSON Web Tokens, JSON Web Keys, and User Info Endpoint.

paths:
/.well-known/authzen-configuration:
get:
tags:
- AuthZEN Access Evaluation Discovery
summary: Gets Access Evaluation configuration data.
description: Gets Access Evaluation configuration data.
operationId: authzen-configuration
responses:
200:
description: OK
content:
application/json:
schema:
type: object
description: AuthZEN Access Evaluation Discovery
required:
- access_evaluation_v1_endpoint
properties:
access_evaluation_v1_endpoint:
type: string
description: "URL of the Access Evaluation endpoint."
example: https://sample.com/jans-auth/restv1/evaluate
500:
description: Error during request processing endpoint.
content:
application/json:
schema:
type: object
required:
- error
- error_description
properties:
error:
type: string
format: enum
example:
- server_error
error_description:
type: string
details:
type: string
/evaluation:
post:
tags:
Expand Down Expand Up @@ -982,6 +1023,11 @@ paths:
type: string
introspection_endpoint:
type: string
description: "URL of the Introspection endpoint."
access_evaluation_v1_endpoint:
type: string
description: "URL of the Access Evaluation endpoint."
example: https://sample.com/jans-auth/restv1/evaluate
auth_level_mapping:
type: object
additionalProperties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ public class AppConfiguration implements Configuration {
@DocProperty(description = "Access evaluation custom script name.")
private String accessEvaluationScriptName;

@DocProperty(description = "Lifetime of access evaluation discovery cache (/.well-known/authzen-configuration).", defaultValue = "5")
private int accessEvaluationDiscoveryCacheLifetimeInMinutes = 5;

@DocProperty(description = "Boolean value true encrypts request object", defaultValue = "false")
private Boolean requireRequestObjectEncryption = false;

Expand Down Expand Up @@ -3532,6 +3535,15 @@ public AppConfiguration setAccessEvaluationAllowBasicClientAuthorization(Boolean
return this;
}

public int getAccessEvaluationDiscoveryCacheLifetimeInMinutes() {
return accessEvaluationDiscoveryCacheLifetimeInMinutes;
}

public AppConfiguration setAccessEvaluationDiscoveryCacheLifetimeInMinutes(int accessEvaluationDiscoveryCacheLifetimeInMinutes) {
this.accessEvaluationDiscoveryCacheLifetimeInMinutes = accessEvaluationDiscoveryCacheLifetimeInMinutes;
return this;
}

public String getAccessEvaluationScriptName() {
return accessEvaluationScriptName;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.jans.as.server.authzen.ws.rs;

import io.jans.as.model.common.FeatureFlagType;
import io.jans.as.model.configuration.AppConfiguration;
import io.jans.as.model.error.ErrorResponseFactory;
import io.jans.as.server.model.common.ExecutionContext;
import io.jans.as.server.service.DiscoveryService;
import io.jans.as.server.service.LocalResponseCache;
import io.jans.as.server.service.external.ExternalAccessEvaluationDiscoveryService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.json.JSONObject;
import org.slf4j.Logger;

import static io.jans.as.model.configuration.ConfigurationResponseClaim.ACCESS_EVALUATION_V1_ENDPOINT;

/**
* @author Yuriy Z
*/
@ApplicationScoped
public class AccessEvaluationDiscoveryService {

@Inject
private Logger log;

@Inject
private ErrorResponseFactory errorResponseFactory;

@Inject
private AppConfiguration appConfiguration;

@Inject
private LocalResponseCache localResponseCache;

@Inject
private ExternalAccessEvaluationDiscoveryService externalAccessEvaluationDiscoveryService;

public JSONObject discovery(ExecutionContext context) {
errorResponseFactory.validateFeatureEnabled(FeatureFlagType.ACCESS_EVALUATION);

final JSONObject cachedResponse = localResponseCache.getAccessEvaluationDiscoveryResponse();
if (cachedResponse != null) {
log.trace("Cached access evaluation discovery response returned.");
return cachedResponse;
}

JSONObject jsonObj = createResponse();
JSONObject clone = new JSONObject(jsonObj.toString());

if (!externalAccessEvaluationDiscoveryService.modifyDiscovery(jsonObj, context)) {
jsonObj = clone; // revert to original state if object was modified in script
}

localResponseCache.putAccessEvaluationDiscoveryResponse(jsonObj);
return jsonObj;
}

private JSONObject createResponse() {
JSONObject jsonObj = new JSONObject();
if (appConfiguration.isFeatureEnabled(FeatureFlagType.ACCESS_EVALUATION))
jsonObj.put(ACCESS_EVALUATION_V1_ENDPOINT, DiscoveryService.getAccessEvaluationV1Endpoint(appConfiguration));

return jsonObj;
}
}
Loading

0 comments on commit efb7ab6

Please sign in to comment.