Skip to content

Latest commit

 

History

History
324 lines (244 loc) · 11.1 KB

File metadata and controls

324 lines (244 loc) · 11.1 KB

Cedarling Java binding

This guide explores the process of generating the Kotlin binding for Cedarling using Cedarling UniFFI. The Kotlin binding is then wrapped in a Java class to enable convenient use in Java applications.

Installation

Building from Source

Prerequisites:

Building from Kotlin binding

  1. Build Cedarling by executing below command from ./jans/jans-cedarling of cloned jans project:
cargo build -r -p cedarling_uniffi

In target/release, you should find the libcedarling_uniffi.dylib (if Mac OS), libcedarling_uniffi.so (if Linux OS), or libcedarling_uniffi.dll (if Windows OS) file, depending on the operating system you are using.

  1. Generate the bindings for Kotlin by running the command below. Replace {build_file} with libcedarling_uniffi.dylib, libcedarling_uniffi.so, or libcedarling_uniffi.dll, depending on which file is generated in target/release.
cargo run --bin uniffi-bindgen generate --library ./target/release/{build_file} --language kotlin --out-dir ./bindings/cedarling-java/src/main/kotlin/io/jans/cedarling
  1. Copy the generated libcedarling_uniffi.dylib, libcedarling_uniffi.so, or libcedarling_uniffi.dll file to resource directory of the cedarling-java Maven project. Replace {build_file} in the below commad with libcedarling_uniffi.dylib, libcedarling_uniffi.so, or libcedarling_uniffi.dll, depending on which file is generated in target/release.
mkdir ./bindings/cedarling-java/src/main/resources
cp ./target/release/{build_file} ./bindings/cedarling-java/src/main/resources
  1. Change directory to ./bindings/cedarling-java and run below command to build cedarling-java jar file. This will generate cedarling-java-{version}-distribution.jar at ./bindings/cedarling-java/target/.
 mvn clean install

Using Cedarling-java Maven dependency

To use Cedarling Java bindings in Java Maven Project add following repository and dependency in pom.xml of the project

    <repositories>
        <repository>
            <id>jans</id>
            <name>Janssen project repository</name>
            <url>https://maven.jans.io/maven</url>
        </repository>
    </repositories>
        <dependency>
            <groupId>io.jans</groupId>
            <artifactId>cedarling-java</artifactId>
            <version>{latest-jans-stable-version}</version>
        </dependency>

Recipes

Using the Cedarling Java binding in custom scripts on the Janssen Auth Server (VM installation).

Here is a simple recipe to add scopes in access-token using update_token script only if the requesting client has authorization_code grant-type. We will use below policy for this:

Policies

@id("Allow_authorization_code")
permit (
  principal is Jans::Workload,
  action == Jans::Action::"Execute",
  resource is Jans::Application
)
when {
  principal.grantTypes.contains("authorization_code")
};

Schema

namespace Jans {
  type Context = {
    current_time?: Long,
    device_health?: Set<String>,
    fraud_indicators?: Set<String>,
    geolocation?: Set<String>,
    network?: String,
    network_type?: String,
    operating_system?: String,
    user_agent?: String
  };

  type Url = __cedar::String;

  type email_address = {
    domain: String,
    uid: String
  };

  entity Application = {
    grantTypes: Set<String>
  };

  entity Role;

  entity TrustedIssuer = {
    issuer_entity_id: Url
  };

  entity User in [Role] = {
    email?: email_address,
    role: Set<String>,
    sub?: String
  };

  entity Workload = {
    client_id: String,
    grantTypes: Set<String>,
    iss?: TrustedIssuer,
    name?: String,
    rp_id?: String,
    spiffe_id?: String
  };

  action "Execute" appliesTo {
    principal: [Workload],
    resource: [Application],
    context: Context
  };
}

Steps:

  1. Upload bootstrap.json and update_token_script.cjar at /opt/jans/jetty/jans-auth/custom/static location of the auth server.

  2. Upload the generated cedarling-java-{version}-distribution.jar at /opt/jans/jetty/jans-auth/custom/libs location of the auth server. Rather than building the cedarling-java-{version}.jar from source code, you can directly download the latest version of the jar from the Maven repository.

  3. The following java Update Token script has been created for calling Cedarling authorization. Enable the script with following Custom Properties:

    Key Values
    BOOTSTRAP_JSON_PATH ./custom/static/bootstrap.json
  4. Map the script with client used to perform authentication.

  1. The script runs before access_token generation and includes openid and profile scopes into the token if the oidc client has authorization_code in grant-types.

Configuration

Policy Store Sources

Cedarling supports multiple ways to load policy stores:

Legacy Single-File Formats

{
  "CEDARLING_POLICY_STORE_LOCAL_FN": "/path/to/policy-store.json",
  "CEDARLING_POLICY_STORE_URI": "https://lock-server.example.com/policy-store"
}

New Directory-Based Format

Policy stores can be structured as directories with human-readable Cedar files:

policy-store/
├── metadata.json           # Required: Store metadata (id, name, version)
├── manifest.json           # Optional: File checksums for integrity validation
├── schema.cedarschema      # Required: Cedar schema (human-readable)
├── policies/               # Required: .cedar policy files
│   ├── allow-read.cedar
│   └── deny-guest.cedar
├── templates/              # Optional: .cedar template files
├── entities/               # Optional: .json entity files
└── trusted-issuers/        # Optional: .json issuer configurations

metadata.json structure:

{
  "cedar_version": "4.4.0",
  "policy_store": {
    "id": "abc123def456",
    "name": "My Application Policies",
    "version": "1.0.0"
  }
}

Cedar Archive (.cjar) Format

Policy stores can be packaged as .cjar files (ZIP archives) for easy distribution.

Testing Configuration

For testing scenarios, you may want to disable JWT validation. You can configure this in your bootstrap configuration:

{
  "CEDARLING_JWT_SIG_VALIDATION": "disabled",
  "CEDARLING_JWT_STATUS_VALIDATION": "disabled"
}

For complete configuration documentation, see cedarling-properties.md.

Context Data API

The Context Data API allows you to push external data into the Cedarling evaluation context, making it available in Cedar policies through the context.data namespace.

These methods are available on the underlying UniFFI-generated Cedarling instance returned by getCedarling(). In Java/Kotlin bindings, JsonValue is represented as a plain String.

import uniffi.cedarling_uniffi.*;
import org.json.JSONObject;
import java.util.List;

CedarlingAdapter adapter = new CedarlingAdapter();
adapter.loadFromJson(bootstrapJson);
Cedarling cedarling = adapter.getCedarling();

// Push data with optional TTL (in seconds)
// The TTL parameter is a nullable Long representing seconds.
// Pass null to use the default TTL from configuration, or pass a Long value for a custom TTL.
String value = "{\"role\":[\"admin\",\"editor\"],\"country\":\"US\"}";
cedarling.pushDataCtx("user:123", value, null);  // null uses default TTL
cedarling.pushDataCtx("config:app", value, 300L);  // Custom TTL: 300 seconds (5 minutes)

// Get data
String result = cedarling.getDataCtx("user:123");
if (result != null) {
    JSONObject data = new JSONObject(result);
}

// Get data entry
DataEntry entry = cedarling.getDataEntryCtx("user:123");
if (entry != null) {
    System.out.println("Key: " + entry.getKey());
    System.out.println("Data type: " + entry.getDataType());
    System.out.println("Created at: " + entry.getCreatedAt());
}

// Remove / clear / list / stats
boolean removed = cedarling.removeDataCtx("user:123");
cedarling.clearDataCtx();
List<DataEntry> entries = cedarling.listDataCtx();
DataStoreStats stats = cedarling.getStatsCtx();

Using Data in Cedar Policies

Data pushed via the Context Data API is automatically available in Cedar policies under the context.data namespace:

permit(
    principal,
    action == Action::"read",
    resource
) when {
    context.data has principal.id &&
    context.data[principal.id].role.contains("admin")
};

The data is injected into the evaluation context before policy evaluation, allowing policies to make decisions based on dynamically pushed data.

Error Handling

The Context Data API methods throw DataException with specific variants:

  • DataException.InvalidKey() - Thrown when the key is empty or invalid
  • DataException.KeyNotFound(String key) - Thrown when attempting to access a non-existent key
  • DataException.StorageLimitExceeded(Long max) - Thrown when the storage limit is exceeded
  • DataException.TtlExceeded(Long provided, Long max) - Thrown when the provided TTL exceeds the maximum allowed TTL
  • DataException.ValueTooLarge(Long size, Long max) - Thrown when the value size exceeds the maximum allowed size
  • DataException.SerializationException(String message) - Thrown when serialization/deserialization fails

Example error handling:

try {
    cedarling.pushDataCtx("", "{\"data\":\"value\"}", null); // Empty key
} catch (DataException.InvalidKey e) {
    System.out.println("Invalid key provided");
} catch (DataException.StorageLimitExceeded e) {
    System.out.println("Storage limit exceeded: max=" + e.getMax());
} catch (DataException.SerializationException e) {
    System.out.println("Serialization error: " + e.getMessage());
} catch (DataException e) {
    System.out.println("Data operation failed: " + e);
}

Note: The CedarlingAdapter wrapper performs additional validation and may throw DataException.InvalidKey() for null or empty keys, or DataException.SerializationException() for null values before calling the underlying Rust implementation.


## Trusted Issuer Loading Info

`CedarlingAdapter` exposes trusted issuer loading status methods:

```java
boolean loadedByName = adapter.isTrustedIssuerLoadedByName("issuer_id");
boolean loadedByIss = adapter.isTrustedIssuerLoadedByIss("https://issuer.example.org");
long totalIssuers = adapter.totalIssuers();
long loadedCount = adapter.loadedTrustedIssuersCount();
List<String> loadedIds = adapter.loadedTrustedIssuerIds();
List<String> failedIds = adapter.failedTrustedIssuerIds();

These methods are useful when your policy store includes trusted-issuers/ definitions.