JanssenProject / jans

An open source enterprise digital identity platform for CIAM or workforce... Janssen is a distribution of standards-based, developer friendly, components that are engineered to work together in any cloud. #OAuth #OpenID #FIDO
https://docs.jans.io
Apache License 2.0
478 stars 75 forks source link

feat(jans-cedarling): Bootstrap support for JSON and YAML properties #10216

Open rmarinn opened 4 days ago

rmarinn commented 4 days ago

Prepare


Description

This PR implements serde::Deserialize for BootstrapConfig, enabling an alternative way of creating a BootStrapConfig struct instead of just instantiating one through a Builder pattern via code.

Target issue

target issue: #10200

closes #10200

Implementation Details

1. Deserialization

Implementing serde::Deserialize for bootstrap config now enables us to deserialize a JSON or YAML string into a BootStrapConfig struct.

// From JSON
let config_json = include_str!("../../../test_files/bootstrap_jwt_disabled.json");
let config_from_json = serde_json::from_str::<BootstrapConfig>(config_json)
    .expect("Should deserialize bootstrap config from JSON");

// From YAML
let config_yaml = include_str!("../../../test_files/bootstrap_jwt_disabled.yaml");
let config_from_yaml = serde_yml::from_str::<BootstrapConfig>(config_json)
    .expect("Should deserialize bootstrap config from YAML");

And for the users, they can use the convenient load_from_file method:

use cedarling::BootstrapConfig;

// works for JSON files
let config = BootstrapConfig::load_from_file("config.json").unwrap();

// or YAML files
let config = BootstrapConfig::load_from_file("config.yaml").unwrap();
2. Loading Policy Store From File

Loading a policy store from a file was introduced by a new bootstrap property and this feature was implemented by updating the load_policy_store function.

// from JSON
let policy_store_from_json_file = load_policy_store(&PolicyStoreConfig {
    source: crate::PolicyStoreSource::FileJson(
        "../test_files/policy-store_generated.json".to_string(),
    ),
})
.expect("Should load policy store from JSON file");

// from YAML
let policy_store_from_yaml_file = load_policy_store(&PolicyStoreConfig {
    source: crate::PolicyStoreSource::FileYaml(
        "../test_files/policy-store_ok.yaml".to_string(),
    ),
})
.expect("Should load policy store from YAML file");
3. Python Bindings

The python bindings required a complex setup to load a BootstrapConfig class. This PR simplifies the BootstrapConfig implementation in the Python bindings into a "flat" class to simplify things. Additionally, TryInto<cedarling::BootstrapConfig> was implemented for the class to be able to easily convert to the core code's BootstrapConfig struct.

Loading the bootstrap configs via code should now look like this in python:

# Initialize a BootstrapConfig instance with the default configuration
bootstrap_config = BootstrapConfig(
    application_name="TestApp",
    policy_store_id="asdasd123123",
    policy_store_local_fn=policy_store_location,
)

# By default, JWT validation is enabled for security.
# If you wish to disable all JWT validation checks, 
# use the `disable_all_jwt_validation` method:
bootstrap_config.disable_all_jwt_validation()

# Alternatively, you can disable specific JWT validation settings when 
# initializing the config:
bootstrap_config = BootstrapConfig(
    application_name="TestApp",
    policy_store_id="asdasd123123",
    policy_store_local_fn=policy_store_location,
    jwt_sig_validation=False,
    jwt_status_validation=False,
    at_iss_validation=False,
    at_jti_validation=False,
    at_nbf_validation=False,
    idt_iss_validation=False,
    idt_sub_validation=False,
    idt_exp_validation=False,
    idt_iat_validation=False,
    idt_aud_validation=False,
    id_token_trust_mode="none",
    userinfo_iss_validation=False,
    userinfo_aud_validation=False,
    userinfo_sub_validation=False,
    userinfo_exp_validation=False,
)
What about loading from a file in the Python bindings?

I tried implementing this but due to the deeply BootstrapConfig struct in the core code, it quickly turned into a mess.

The method i tried was to use the core code's BoostrapConfigRaw then load that from a file... which would work but we now have this weird flow when loading a config in the python binding: loads from a core struct -> converts it into the python binding class -> then it's gonna get converted back into the core struct again when Cedarling gets initialized.

Then i realized that this might be better to implement in another issue since it feels too out of scope already.


Test and Document the changes

Please check the below before submitting your PR. The PR will not be merged if there are no commits that start with docs: to indicate documentation changes or if the below checklist is not selected.

dryrunsecurity[bot] commented 4 days ago

DryRun Security Summary

The pull request focuses on improving the security of the Cedarling application, including extensive configuration options for JWT validation, policy store management, logging and monitoring, and dependency management.

Expand for full summary
**Summary:** The code changes in this pull request focus on various security-related aspects of the Cedarling application, including: 1. **JWT Validation Configuration**: The changes introduce extensive configuration options for validating JWT tokens, such as enabling/disabling signature validation, status validation, and various claim validations. This allows the application to have fine-grained control over the JWT validation process, which is crucial for ensuring the integrity and authenticity of the tokens used for authentication and authorization. 2. **Policy Store Management**: The code includes improvements to the policy store configuration, allowing the application to load policies from various sources, including JSON, YAML, and a centralized Lock Master service. The policy store is a critical component for the application's authorization decisions, and the changes help ensure the security and reliability of this functionality. 3. **Logging and Monitoring**: The changes include options for configuring the application's logging, including the ability to send logs to the Lock Master service. Proper logging and monitoring are essential for detecting and investigating potential security incidents. 4. **Dependency Management**: The code updates the handling of external dependencies, such as the `jsonwebtoken` crate, to ensure that the supported algorithms and validation mechanisms are properly configured and secured. Overall, the changes in this pull request demonstrate a strong focus on improving the security posture of the Cedarling application, with a particular emphasis on the handling of JWTs, policy management, and logging/monitoring capabilities. These improvements are essential for maintaining the application's security and reliability in production environments. **Files Changed:** - `Cargo.toml`: A new dependency, `jsonwebtoken` version 9.3.0, has been added, indicating the use of JSON Web Tokens (JWT) in the application. - `PYTHON_TYPES.md`: The `BootstrapConfig` class has been significantly expanded, with new attributes related to policy store settings, logging configuration, authorization settings, JWT validation, and integration with the Lock Master service. - `cedarling_python.pyi`: The `BootstrapConfig` class has been updated with new configuration options for JWT validation, token validation, user and workload authorization, policy store, and integration with the Lock Master service. - `cedarling-properties.md`: The configuration file for the Cedarling application has been updated with new properties related to JWT validation, policy store, Lock Master integration, and logging/auditing. - `print_documentation.py`: This script has been updated to remove imports for various configuration-related classes, focusing only on the `BootstrapConfig` and related classes. - `jwt_config.rs`: The `JwtConfig` struct has been updated to handle the configuration of supported JWT signature algorithms. - `bootstrap_config.rs`: The `BootstrapConfig` struct has been expanded to include more security-related configuration options, such as JWT validation, policy store management, and Lock Master integration. - `authorize_with_jwt_validation.rs`: The example code demonstrates the configuration of JWT validation, including the specification of supported signing algorithms. - `decode.rs`: The `BootstrapConfig` parsing and validation logic has been updated to handle the new security-related configuration options. - `log_config.rs`: The `LogConfig` and related structs have been made `PartialEq` to allow for comparison of these types. - `cedar_schema.rs`: The `CedarSchema` struct and its deserialization logic have been updated to improve the handling of the schema and JSON representation. - `cedar_json.rs`: The visibility of the `CedarSchemaJson` struct has been changed from `pub(crate)` to `pub`. - `init/mod.rs`: The `jwt_algorithm` module has been removed, indicating a change in the way JWTs are handled or validated in the application. - `policy_store.rs`: The `load_policy_store()` function has been updated to support loading the policy store from various sources, including JSON, YAML, and a Lock Master service. - `service_config.rs`: The `ServiceConfig` struct has been updated to handle the configuration of JWT algorithms and the fetching of trusted issuers and OpenID configurations. - `decoding_strategy.rs`: The JWT decoding strategy has been updated to support two modes: one with validation and one without, providing more flexibility in the JWT handling process. - `jwt_service_config.rs`: The `JwtServiceConfig` enum has been updated to use a `HashSet` for the list of supported algorithms, improving the efficiency of the algorithm lookup process. - `mod.rs`: The `

Code Analysis

We ran 9 analyzers against 30 files and 0 analyzers had findings. 9 analyzers had no findings.

Riskiness

:green_circle: Risk threshold not exceeded.

View PR in the DryRun Dashboard.

olehbozhok commented 9 hours ago

Why we need bindings\cedarling_python\src\config\jwt_config.rs if we use jwt_sig_validation in bootstrap config?

olehbozhok commented 9 hours ago

It would be great to understand why BootstrapConfigRaw in unused in bindings?

Actually we can use **kwargs in python to collect all parameters and deserialize (using serde-pyobject) it to BootstrapConfigRaw, and hold this value as inner (usual pattern in rust)

olehbozhok commented 5 hours ago

Also, we need to understand if user specify some parameter or not. To add seamlessly loading parameters from env vars and check if it was not defined by user, try to load it from env and if not present in env vars use default value.

Or in other priority...