Skip to content

Commit

Permalink
Merge branch 'main' into cn-prometheus-lib
Browse files Browse the repository at this point in the history
  • Loading branch information
moabu authored Dec 19, 2024
2 parents 7ab06a4 + 96e5d24 commit 940847f
Show file tree
Hide file tree
Showing 7 changed files with 663 additions and 74 deletions.
554 changes: 514 additions & 40 deletions charts/janssen/values.schema.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions charts/janssen/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,8 @@ global:
lockConfigEnabled: false
# -- Enable endpoint /jans-lock
lockEnabled: false
# -- Enable endpoint /.well-known/authzen-configuration
authzenConfigEnabled: true
# -- Lock config ingress resource labels. key app is taken
lockConfigLabels: { }
# -- Lock config ingress resource additional annotations.
Expand Down Expand Up @@ -814,8 +816,6 @@ global:
u2fConfigLabels: { }
# -- u2f config ingress resource additional annotations.
u2fAdditionalAnnotations: { }
# -- Enable endpoint /.well-known/authzen-configuration
authzenConfigEnabled: true
# -- authzen config ingress resource labels. key app is taken
authzenConfigLabels: { }
# -- authzen config ingress resource additional annotations.
Expand Down
10 changes: 3 additions & 7 deletions docs/cedarling/cedarling-policy-store.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The Cedarling Policy Store uses a JSON file named `cedarling_store.json` to stor

**Note:** The `cedarling_store.json` file is only needed if the bootstrap properties: `CEDARLING_LOCK`; `CEDARLING_POLICY_STORE_URI`; and `CEDARLING_POLICY_STORE_ID` are not set to a local location. If you're fetching the policies remotely, you don't need a `cedarling_store.json` file.

### JSON Schema
## JSON Schema

The JSON Schema accepted by cedarling is defined as follows:

Expand Down Expand Up @@ -206,12 +206,8 @@ The Token Entity Metadata Schema defines how tokens are mapped, parsed, and tran

- **role_mapping**: (String OR Array of String, *Optional*) Indicates which field in the token should be used for role-based access control. If not needed, set to an empty string (`""`).

You can include a `role_mapping` in each token but only the first one that get parsed will be recognized by Cedarling. Cedarling parses the `role_mapping`s for each token in this order:

1. `access_tokens`
2. `id_tokens`
3. `userinfo_tokens`
4. `tx_tokens`
You can include a `role_mapping` in each token, all of them will be executed by Cedarling.
If none `role_mapping` defined the `Cedarling` will try to find role in `userinfo` token in field `role`.

#### Claim mapping

Expand Down
33 changes: 27 additions & 6 deletions jans-cedarling/cedarling/src/authz/entities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::common::cedar_schema::CedarSchemaJson;

use crate::authz::token_data::{AccessTokenData, IdTokenData, UserInfoTokenData};
use crate::common::policy_store::{
AccessTokenEntityMetadata, ClaimMappings, PolicyStore, TokenKind, TrustedIssuer,
AccessTokenEntityMetadata, ClaimMappings, PolicyStore, RoleMapping, TokenKind, TrustedIssuer,
};
use crate::jwt;
use cedar_policy::EntityUid;
Expand Down Expand Up @@ -194,15 +194,36 @@ pub enum RoleEntityError {
},
}

/// Create `Role` entity from based on `TrustedIssuer` or default value of `RoleMapping`
/// Create `Role` entites from based on `TrustedIssuer` role mapping for each token or default value of `RoleMapping`
pub fn create_role_entities(
policy_store: &PolicyStore,
tokens: &ProcessTokensResult,
trusted_issuer: &TrustedIssuer,
) -> Result<Vec<cedar_policy::Entity>, RoleEntityError> {
// get role mapping or default value
let role_mapping = trusted_issuer.get_role_mapping().unwrap_or_default();
let mut role_entities = Vec::new();

// Iterate via role mappings for each token or default `RoleMapping` value
let role_mappings = trusted_issuer
.get_role_mapping()
.unwrap_or(vec![RoleMapping::default()]);

for role_mapping in role_mappings {
let mut entities =
extract_roles_from_tokens(policy_store, role_mapping, tokens, trusted_issuer)?;
// Moves all the elements to `role_entities`
role_entities.append(&mut entities);
}

Ok(role_entities)
}

/// Extract `Role` entites based on single `RoleMapping`
fn extract_roles_from_tokens(
policy_store: &PolicyStore,
role_mapping: RoleMapping,
tokens: &ProcessTokensResult,
trusted_issuer: &TrustedIssuer,
) -> Result<Vec<cedar_policy::Entity>, RoleEntityError> {
let parsed_typename = EntityParsedTypeName::new("Role", policy_store.namespace());
let role_entity_type = parsed_typename.full_type_name();

Expand All @@ -215,7 +236,7 @@ pub fn create_role_entities(
return Err(RoleEntityError::Create {
error: CedarPolicyCreateTypeError::TransactionToken,
token_kind: TokenKind::Transaction,
})
});
},
};

Expand All @@ -228,7 +249,7 @@ pub fn create_role_entities(
return Err(RoleEntityError::Create {
error: CedarPolicyCreateTypeError::TransactionToken,
token_kind: TokenKind::Transaction,
})
});
},
};

Expand Down
38 changes: 20 additions & 18 deletions jans-cedarling/cedarling/src/common/policy_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ impl Default for &TrustedIssuer {
}

/// Structure define the source from where role mappings are retrieved.
#[derive(Debug)]
pub struct RoleMapping<'a> {
pub kind: TokenKind,
pub mapping_field: &'a str,
Expand Down Expand Up @@ -172,43 +173,43 @@ impl Default for UserMapping<'_> {
impl TrustedIssuer {
/// Retrieves the available `RoleMapping` from the token metadata.
///
/// Checks each token metadata and returns the first one found with a `role_mapping` field.
///
/// The checks happen in this order:
/// 1. access_token
/// 2. id_token
/// 3. userinfo_token
/// 4. tx_token
pub fn get_role_mapping(&self) -> Option<RoleMapping> {
/// Checks each token metadata and returns all found in a `role_mapping` field.
pub fn get_role_mapping(&self) -> Option<Vec<RoleMapping>> {
let mut role_mapping_vec = Vec::new();

if let Some(role_mapping) = &self.access_tokens.entity_metadata.role_mapping {
return Some(RoleMapping {
role_mapping_vec.push(RoleMapping {
kind: TokenKind::Access,
mapping_field: role_mapping.as_str(),
});
}

if let Some(role_mapping) = &self.id_tokens.role_mapping {
return Some(RoleMapping {
role_mapping_vec.push(RoleMapping {
kind: TokenKind::Id,
mapping_field: role_mapping.as_str(),
});
}

if let Some(role_mapping) = &self.userinfo_tokens.role_mapping {
return Some(RoleMapping {
role_mapping_vec.push(RoleMapping {
kind: TokenKind::Userinfo,
mapping_field: role_mapping.as_str(),
});
}

if let Some(role_mapping) = &self.tx_tokens.role_mapping {
return Some(RoleMapping {
role_mapping_vec.push(RoleMapping {
kind: TokenKind::Transaction,
mapping_field: role_mapping.as_str(),
});
}
};

None
if !role_mapping_vec.is_empty() {
Some(role_mapping_vec)
} else {
None
}
}

/// Retrieves the available `user id` mapping from the token metadata.
Expand Down Expand Up @@ -315,10 +316,11 @@ impl<'de> Deserialize<'de> for TokenKind {
"id_token" => Ok(TokenKind::Id),
"userinfo_token" => Ok(TokenKind::Userinfo),
"access_token" => Ok(TokenKind::Access),
_ => Err(serde::de::Error::unknown_variant(
&token_kind,
&["access_token", "id_token", "userinfo_token"],
)),
_ => Err(serde::de::Error::unknown_variant(&token_kind, &[
"access_token",
"id_token",
"userinfo_token",
])),
}
}
}
Expand Down
80 changes: 79 additions & 1 deletion jans-cedarling/cedarling/src/tests/mapping_entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
//! CEDARLING_MAPPING_USERINFO_TOKEN
use super::utils::*;
use crate::{cmp_decision, cmp_policy, CedarPolicyCreateTypeError};
use crate::{AuthorizeError, Cedarling};
use crate::{CedarPolicyCreateTypeError, cmp_decision, cmp_policy};
use cedarling_util::get_raw_config;
use std::collections::HashSet;
use std::sync::LazyLock;
use test_utils::assert_eq;

Expand Down Expand Up @@ -295,3 +296,80 @@ fn test_failed_userinfo_token_mapping() {
"should be error CouldNotFindEntity"
);
}

/// Check if we get roles mapping from all tokens.
/// Because we specify mapping from each token in policy store
/// We use iss in JWT tokens to enable mapping for trusted issuer in policy store
#[test]
fn test_role_many_tokens_mapping() {
let raw_config = get_raw_config(POLICY_STORE_RAW_YAML);

let config = crate::BootstrapConfig::from_raw_config(&raw_config)
.expect("raw config should parse without errors");

let cedarling = Cedarling::new(&config).expect("could be created without error");

let request = // deserialize `Request` from json
Request::deserialize(serde_json::json!(
{
"access_token": generate_token_using_claims(json!({
"org_id": "some_long_id",
"jti": "some_jti",
"client_id": "some_client_id",
"iss": "https://test-casa.gluu.info",
"aud": "some_aud",
"role": "Guest",
})),
"id_token": generate_token_using_claims(json!({
"jti": "some_jti",
"iss": "https://test-casa.gluu.info",
"aud": "some_aud",
"sub": "some_sub",
"role": "User",
})),
"userinfo_token": generate_token_using_claims(json!({
"jti": "some_jti",
"country": "US",
"sub": "some_sub",
"iss": "https://test-casa.gluu.info",
"client_id": "some_client_id",
"role": "Admin",
})),
"action": "Jans::Action::\"Update\"",
"resource": {
"id": "random_id",
"type": "Jans::Issue",
"org_id": "some_long_id",
"country": "US"
},
"context": {},
}
))
.expect("Request should be deserialized from json");

// HashSet of expected roles
let mut expected_role_ids: HashSet<String> =
HashSet::from_iter(["Guest", "User", "Admin"].into_iter().map(String::from));

// iterate over roles that created and filter expected roles
let roles_left = cedarling
.authorize_entities_data(&request)
.expect("should get authorize_entities_data without errors")
.role_entities
.into_iter()
.map(|entity| entity.uid().id().escaped())
// if role successfully removed from `expected_role_ids` we filter it
.filter(|uid| !expected_role_ids.remove(uid.as_str()))
.map(|v| v.to_string())
.collect::<Vec<_>>();

assert!(
expected_role_ids.is_empty(),
"HashSet `expected_role_ids` should be empty, not created roles: {expected_role_ids:?}"
);

assert!(
roles_left.is_empty(),
"list `roles_left` should be empty, additional created roles: {roles_left:?}"
)
}
18 changes: 18 additions & 0 deletions jans-cedarling/test_files/policy-store_entity_mapping.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,21 @@ policy_stores:
};
}
trusted_issuers:
3e59e1d6f009a16d40a33b2f10fc259d05a1a5d9a922:
name: TestAgama
description: TestAgama trusted issuer
openid_configuration_endpoint: https://test-casa.gluu.info/.well-known/openid-configuration
access_tokens:
trusted: true
principal_identifier: jti
user_id: ''
role_mapping: role
# claim_mapping: {}
id_tokens:
user_id: ''
role_mapping: role
userinfo_tokens:
user_id: ''
role_mapping: role
# claim_mapping: {}

0 comments on commit 940847f

Please sign in to comment.