diff --git a/jans-cedarling/bindings/cedarling_python/PYTHON_TYPES.md b/jans-cedarling/bindings/cedarling_python/PYTHON_TYPES.md index 542450a37d8..c790acec696 100644 --- a/jans-cedarling/bindings/cedarling_python/PYTHON_TYPES.md +++ b/jans-cedarling/bindings/cedarling_python/PYTHON_TYPES.md @@ -205,47 +205,11 @@ error : str The error message describing the evaluation failure. ___ -# authorize_errors.ActionError -Error encountered while parsing Action to EntityUid -___ - # authorize_errors.AuthorizeError Exception raised by authorize_errors ___ -# authorize_errors.BuildContextError -Error encountered while building the request context -___ - -# authorize_errors.BuildEntityError -Error encountered while running on strict id token trust mode -___ - -# authorize_errors.CreateContextError -Error encountered while validating context according to the schema -___ - -# authorize_errors.EntitiesError -Error encountered while collecting all entities -___ - -# authorize_errors.EntitiesToJsonError -Error encountered while parsing all entities to json for logging -___ - -# authorize_errors.IdTokenTrustModeError -Error encountered while running on strict id token trust mode -___ - -# authorize_errors.ProcessTokens -Error encountered while processing JWT token data -___ - -# authorize_errors.UserRequestValidationError -Error encountered while creating cedar_policy::Request for user entity principal -___ - -# authorize_errors.WorkloadRequestValidationError -Error encountered while creating cedar_policy::Request for workload entity principal +# authorize_errors.LoggingError +Error encountered while trying to write logs ___ diff --git a/jans-cedarling/bindings/cedarling_python/cedarling_python.pyi b/jans-cedarling/bindings/cedarling_python/cedarling_python.pyi index 1aa56d25658..db914a1f006 100644 --- a/jans-cedarling/bindings/cedarling_python/cedarling_python.pyi +++ b/jans-cedarling/bindings/cedarling_python/cedarling_python.pyi @@ -191,6 +191,8 @@ class AuthorizeResult: def person(self) -> AuthorizeResultResponse | None: ... + def reason_input(self) -> String | None: ... + @final class AuthorizeResultResponse: diff --git a/jans-cedarling/bindings/cedarling_python/example.py b/jans-cedarling/bindings/cedarling_python/example.py index 4c95c748f61..7c0493c5d00 100644 --- a/jans-cedarling/bindings/cedarling_python/example.py +++ b/jans-cedarling/bindings/cedarling_python/example.py @@ -238,16 +238,16 @@ def load_yaml_to_env(yaml_path): print() -# watch on the decision for person -person_result = authorize_result.person() -print(f"Result of person authorization: {person_result.decision}") -person_diagnostic = person_result.diagnostics +# watch on the decision for user +user_result = authorize_result.user() +print(f"Result of user authorization: {user_result.decision}") +user_diagnostic = user_result.diagnostics print("Policy ID used:") -for diagnostic in person_diagnostic.reason: +for diagnostic in user_diagnostic.reason: print(diagnostic) -print(f"Errors during authorization: {len(person_diagnostic.errors)}") -for diagnostic in person_diagnostic.errors: +print(f"Errors during authorization: {len(user_diagnostic.errors)}") +for diagnostic in user_diagnostic.errors: print(diagnostic) print() diff --git a/jans-cedarling/bindings/cedarling_python/src/authorize/authorize_result.rs b/jans-cedarling/bindings/cedarling_python/src/authorize/authorize_result.rs index a83372f48d1..712465b8136 100644 --- a/jans-cedarling/bindings/cedarling_python/src/authorize/authorize_result.rs +++ b/jans-cedarling/bindings/cedarling_python/src/authorize/authorize_result.rs @@ -38,9 +38,14 @@ impl AuthorizeResult { self.inner.workload.clone().map(|v| v.into()) } - /// Get the decision value for person/user - fn person(&self) -> Option { - self.inner.person.clone().map(|v| v.into()) + /// Get the decision value for user + fn user(&self) -> Option { + self.inner.user.clone().map(|v| v.into()) + } + + /// The reason why the result was DENY in case it was because of a bad input + fn reason_input(&self) -> Option { + self.inner.reason_input.as_ref().map(|e| e.to_string()) } } diff --git a/jans-cedarling/bindings/cedarling_python/src/authorize/errors.rs b/jans-cedarling/bindings/cedarling_python/src/authorize/errors.rs index 06534720bf5..23e1f07fec7 100644 --- a/jans-cedarling/bindings/cedarling_python/src/authorize/errors.rs +++ b/jans-cedarling/bindings/cedarling_python/src/authorize/errors.rs @@ -18,79 +18,9 @@ create_exception!( create_exception!( authorize_errors, - DecodeTokens, + LoggingError, AuthorizeError, - "Error encountered while decoding JWT token data" -); - -create_exception!( - authorize_errors, - ProcessTokens, - AuthorizeError, - "Error encountered while processing JWT token data" -); - -create_exception!( - authorize_errors, - ActionError, - AuthorizeError, - "Error encountered while parsing Action to EntityUid" -); - -create_exception!( - authorize_errors, - CreateContextError, - AuthorizeError, - "Error encountered while validating context according to the schema" -); - -create_exception!( - authorize_errors, - WorkloadRequestValidationError, - AuthorizeError, - "Error encountered while creating cedar_policy::Request for workload entity principal" -); - -create_exception!( - authorize_errors, - UserRequestValidationError, - AuthorizeError, - "Error encountered while creating cedar_policy::Request for user entity principal" -); - -create_exception!( - authorize_errors, - EntitiesError, - AuthorizeError, - "Error encountered while collecting all entities" -); - -create_exception!( - authorize_errors, - EntitiesToJsonError, - AuthorizeError, - "Error encountered while parsing all entities to json for logging" -); - -create_exception!( - authorize_errors, - BuildContextError, - AuthorizeError, - "Error encountered while building the request context" -); - -create_exception!( - authorize_errors, - IdTokenTrustModeError, - AuthorizeError, - "Error encountered while running on strict id token trust mode" -); - -create_exception!( - authorize_errors, - BuildEntityError, - AuthorizeError, - "Error encountered while running on strict id token trust mode" + "Error encountered while trying to write logs" ); #[pyclass] @@ -131,16 +61,7 @@ macro_rules! errors_functions { // This function is used to convert `cedarling::AuthorizeError` to a Python exception. // For each possible case of `AuthorizeError`, we have created a corresponding Python exception that inherits from `cedarling::AuthorizeError`. errors_functions! { - ProcessTokens => ProcessTokens, - Action => ActionError, - CreateContext => CreateContextError, - WorkloadRequestValidation => WorkloadRequestValidationError, - UserRequestValidation => UserRequestValidationError, - Entities => EntitiesError, - EntitiesToJson => EntitiesToJsonError, - BuildContext => BuildContextError, - IdTokenTrustMode => IdTokenTrustModeError, - BuildEntity => BuildEntityError + Logging => LoggingError } pub fn authorize_errors_module(m: &Bound<'_, PyModule>) -> PyResult<()> { diff --git a/jans-cedarling/bindings/cedarling_wasm/src/lib.rs b/jans-cedarling/bindings/cedarling_wasm/src/lib.rs index 5fb8c52eaf7..c65fc9505a0 100644 --- a/jans-cedarling/bindings/cedarling_wasm/src/lib.rs +++ b/jans-cedarling/bindings/cedarling_wasm/src/lib.rs @@ -168,7 +168,10 @@ pub struct AuthorizeResult { pub workload: Option, /// Result of authorization where principal is `Jans::User` #[wasm_bindgen(getter_with_clone)] - pub person: Option, + pub user: Option, + + #[wasm_bindgen(getter_with_clone)] + pub reason_input: Option, /// Result of authorization /// true means `ALLOW` @@ -192,9 +195,10 @@ impl From for AuthorizeResult { workload: value .workload .map(|v| AuthorizeResultResponse { inner: Rc::new(v) }), - person: value - .person + user: value + .user .map(|v| AuthorizeResultResponse { inner: Rc::new(v) }), + reason_input: value.reason_input.as_ref().map(|x| x.to_string()), decision: value.decision, } } diff --git a/jans-cedarling/cedarling/src/authz/authorize_result.rs b/jans-cedarling/cedarling/src/authz/authorize_result.rs index 86f0ebf9bc8..dcc474b0bbc 100644 --- a/jans-cedarling/cedarling/src/authz/authorize_result.rs +++ b/jans-cedarling/cedarling/src/authz/authorize_result.rs @@ -5,23 +5,29 @@ * Copyright (c) 2024, Gluu, Inc. */ -use cedar_policy::Decision; -use serde::ser::SerializeStruct; -use serde::{Serialize, Serializer}; -use std::collections::HashSet; - +use super::BadInputError; use crate::bootstrap_config::WorkloadBoolOp; +use cedar_policy::{Decision, PolicyId}; +use serde::{Serialize, Serializer, ser::SerializeStruct}; +use std::collections::HashSet; /// Result of authorization and evaluation cedar policy /// based on the [Request](crate::models::request::Request) and policy store -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Serialize)] pub struct AuthorizeResult { /// Result of authorization where principal is `Jans::Workload` #[serde(serialize_with = "serialize_opt_response")] pub workload: Option, /// Result of authorization where principal is `Jans::User` #[serde(serialize_with = "serialize_opt_response")] - pub person: Option, + pub user: Option, + + /// Reasons why the authorization failed due to malformed inputs + #[serde( + serialize_with = "serialize_reason_input", + skip_serializing_if = "Option::is_none" + )] + pub reason_input: Option, /// Result of authorization /// true means `ALLOW` @@ -66,17 +72,80 @@ where } } +impl From for AuthorizeResult +where + E: Into, +{ + fn from(value: E) -> Self { + Self { + reason_input: Some(value.into()), + decision: false, + workload: None, + user: None, + } + } +} + +#[derive(Debug, Clone, Serialize)] +pub struct PrincipalResult { + pub decision: Option, + pub reason_input: Option, + pub reason_policy: Option>, + #[serde( + serialize_with = "serialize_errors_policy", + skip_serializing_if = "Vec::is_empty" + )] + pub errors_policy: Vec, +} + +impl From for PrincipalResult { + fn from(response: cedar_policy::Response) -> Self { + let reason_policy = response.diagnostics().reason().cloned().collect(); + let errors_policy = response.diagnostics().errors().cloned().collect(); + + Self { + decision: Some(response.decision()), + reason_input: None, + reason_policy: Some(reason_policy), + errors_policy, + } + } +} + +fn serialize_reason_input( + error: &Option, + serializer: S, +) -> Result +where + S: Serializer, +{ + error.as_ref().map(|e| e.to_string()).serialize(serializer) +} + +fn serialize_errors_policy( + errors: &[cedar_policy::AuthorizationError], + serializer: S, +) -> Result +where + S: Serializer, +{ + let str_errors = errors.iter().map(|e| e.to_string()).collect::(); + str_errors.serialize(serializer) +} + impl AuthorizeResult { /// Builder function for AuthorizeResult pub(crate) fn new( user_workload_operator: WorkloadBoolOp, workload: Option, - person: Option, + user: Option, ) -> Self { + let decision = calc_decision(user_workload_operator, &workload, &user); Self { - decision: calc_decision(&user_workload_operator, &workload, &person), + decision, + reason_input: None, + user, workload, - person, } } @@ -98,7 +167,7 @@ impl AuthorizeResult { /// If person and workload is present will be used operator (AND or OR) based on `CEDARLING_USER_WORKLOAD_BOOLEAN_OPERATION` bootstrap property. /// If none present return false. fn calc_decision( - user_workload_operator: &WorkloadBoolOp, + user_workload_operator: WorkloadBoolOp, workload: &Option, person: &Option, ) -> bool { diff --git a/jans-cedarling/cedarling/src/authz/entity_builder/build_user_entity.rs b/jans-cedarling/cedarling/src/authz/entity_builder/build_user_entity.rs index d22e45b87f3..38547d5561e 100644 --- a/jans-cedarling/cedarling/src/authz/entity_builder/build_user_entity.rs +++ b/jans-cedarling/cedarling/src/authz/entity_builder/build_user_entity.rs @@ -53,11 +53,16 @@ impl fmt::Display for BuildUserEntityError { } else { writeln!( f, - "failed to create User Entity due to the following errors:" + "failed to create User Entity due to the following errors: [{}]", + self.errors + .iter() + .map(|(tkn, err)| format!( + "tried building user entity using `{}` but failed: {}", + tkn, err + )) + .collect::>() + .join(", ") )?; - for (token_kind, error) in &self.errors { - writeln!(f, "- TokenKind {:?}: {}", token_kind, error)?; - } } Ok(()) } diff --git a/jans-cedarling/cedarling/src/authz/entity_builder/build_workload_entity.rs b/jans-cedarling/cedarling/src/authz/entity_builder/build_workload_entity.rs index 3f444413a16..99feedd282a 100644 --- a/jans-cedarling/cedarling/src/authz/entity_builder/build_workload_entity.rs +++ b/jans-cedarling/cedarling/src/authz/entity_builder/build_workload_entity.rs @@ -25,11 +25,9 @@ impl EntityBuilder { tokens.access.as_ref(), vec![], ), - ( - DEFAULT_ID_TKN_WORKLOAD_CLAIM, - tokens.id.as_ref(), - vec![ClaimAliasMap::new("aud", "client_id")], - ), + (DEFAULT_ID_TKN_WORKLOAD_CLAIM, tokens.id.as_ref(), vec![ + ClaimAliasMap::new("aud", "client_id"), + ]), ] .into_iter() { @@ -67,11 +65,16 @@ impl fmt::Display for BuildWorkloadEntityError { } else { writeln!( f, - "failed to create Workload Entity due to the following errors:" + "failed to create Workload Entity due to the following errors: [{}]", + self.errors + .iter() + .map(|(tkn, err)| format!( + "tried building workload entity using `{}` but failed: {}", + tkn, err + )) + .collect::>() + .join(", ") )?; - for (token_kind, error) in &self.errors { - writeln!(f, "- TokenKind {:?}: {}", token_kind, error)?; - } } Ok(()) } diff --git a/jans-cedarling/cedarling/src/authz/log_authz.rs b/jans-cedarling/cedarling/src/authz/log_authz.rs new file mode 100644 index 00000000000..ad7a731de28 --- /dev/null +++ b/jans-cedarling/cedarling/src/authz/log_authz.rs @@ -0,0 +1,130 @@ +// This software is available under the Apache-2.0 license. +// See https://www.apache.org/licenses/LICENSE-2.0.txt for full text. +// +// Copyright (c) 2024, Gluu, Inc. + +use super::{AuthorizeResult, Authz, DecodedTokens, LogWriter}; +use crate::log::{ + AuthorizationLogInfo, BaseLogEntry, DecisionLogEntry, DiagnosticsRefs, LogTokensInfo, LogType, + PrincipalLogEntry, UserAuthorizeInfo, WorkloadAuthorizeInfo, +}; +use crate::{LogLevel, Request}; +use cedar_policy::EntityUid; +use cedar_policy::{Entities, entities_errors::EntitiesError}; +use serde_json::Value; +use std::{collections::HashMap, io::Cursor}; + +pub struct LogAuthzArgs<'a> { + pub tokens: DecodedTokens<'a>, + pub entities: &'a Entities, + pub user_authz_info: Option, + pub workload_authz_info: Option, + pub user_entity_claims: Option>, + pub workload_entity_claims: Option>, + pub request: &'a Request, + pub resource: EntityUid, + pub result: &'a AuthorizeResult, + pub elapsed_ms: i64, +} + +impl Authz { + pub fn log_authz(&self, args: LogAuthzArgs) -> Result<(), LoggingError> { + // getting entities as json + let mut entities_raw_json = Vec::new(); + let cursor = Cursor::new(&mut entities_raw_json); + + args.entities.write_to_json(cursor)?; + let entities_json: serde_json::Value = + serde_json::from_slice(entities_raw_json.as_slice())?; + + let user_authz_diagnostic = args + .user_authz_info + .as_ref() + .map(|auth_info| &auth_info.diagnostics); + + let workload_authz_diagnostic = args + .user_authz_info + .as_ref() + .map(|auth_info| &auth_info.diagnostics); + + let tokens_logging_info = LogTokensInfo { + access: args.tokens.access.as_ref().map(|tkn| { + tkn.logging_info( + self.config + .authorization + .decision_log_default_jwt_id + .as_str(), + ) + }), + id_token: args.tokens.id.as_ref().map(|tkn| { + tkn.logging_info( + self.config + .authorization + .decision_log_default_jwt_id + .as_str(), + ) + }), + userinfo: args.tokens.userinfo.as_ref().map(|tkn| { + tkn.logging_info( + self.config + .authorization + .decision_log_default_jwt_id + .as_str(), + ) + }), + }; + + // Decision log + // we log decision log before debug log, to avoid cloning diagnostic info + self.config.log_service.as_ref().log_any(&DecisionLogEntry { + base: BaseLogEntry::new(self.config.pdp_id, LogType::Decision), + policystore_id: self.config.policy_store.id.as_str(), + policystore_version: self.config.policy_store.get_store_version(), + principal: PrincipalLogEntry::new(&self.config.authorization), + user: args.user_entity_claims, + workload: args.workload_entity_claims, + lock_client_id: None, + action: args.request.action.clone(), + resource: args.resource.to_string(), + decision: args.result.decision.into(), + tokens: tokens_logging_info, + decision_time_ms: args.elapsed_ms, + diagnostics: DiagnosticsRefs::new(&[ + &user_authz_diagnostic, + &workload_authz_diagnostic, + ]), + }); + + // DEBUG LOG + // Log all result information about both authorize checks. + // Where principal is `"Jans::Workload"` and where principal is `"Jans::User"`. + self.config.log_service.as_ref().log( + crate::log::LogEntry::new_with_data( + self.config.pdp_id, + Some(self.config.application_name.clone()), + LogType::System, + ) + .set_level(LogLevel::DEBUG) + .set_auth_info(AuthorizationLogInfo { + action: args.request.action.clone(), + context: args.request.context.clone(), + resource: args.resource.to_string(), + entities: entities_json, + person_authorize_info: args.user_authz_info, + workload_authorize_info: args.workload_authz_info, + authorized: args.result.decision, + }) + .set_message("Result of authorize.".to_string()), + ); + + Ok(()) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum LoggingError { + #[error("failed to write entities to json file: {0}")] + WriteEntitiesToJsonFile(#[from] EntitiesError), + #[error("failed to serialize entities into json: {0}")] + SerializeEntitiesToJson(#[from] serde_json::error::Error), +} diff --git a/jans-cedarling/cedarling/src/authz/mod.rs b/jans-cedarling/cedarling/src/authz/mod.rs index 8c7786ae54f..c1041b18620 100644 --- a/jans-cedarling/cedarling/src/authz/mod.rs +++ b/jans-cedarling/cedarling/src/authz/mod.rs @@ -12,26 +12,25 @@ use crate::authorization_config::IdTokenTrustMode; use crate::bootstrap_config::AuthorizationConfig; use crate::common::app_types; use crate::common::policy_store::PolicyStoreWithID; -use crate::jwt::{self, TokenStr}; +use crate::jwt::{self, JwtProcessingError, TokenStr}; use crate::log::interface::LogWriter; use crate::log::{ - AuthorizationLogInfo, BaseLogEntry, DecisionLogEntry, Diagnostics, DiagnosticsRefs, LogEntry, - LogLevel, LogTokensInfo, LogType, Logger, PrincipalLogEntry, UserAuthorizeInfo, - WorkloadAuthorizeInfo, + Diagnostics, LogEntry, LogLevel, LogType, Logger, UserAuthorizeInfo, WorkloadAuthorizeInfo, }; use build_ctx::*; use cedar_policy::{Entities, Entity, EntityUid}; use chrono::Utc; use entity_builder::*; +use log_authz::{LogAuthzArgs, LoggingError}; use request::Request; use std::collections::HashMap; -use std::io::Cursor; use std::str::FromStr; use std::sync::Arc; use trust_mode::*; mod authorize_result; mod build_ctx; +mod log_authz; mod trust_mode; pub(crate) mod entity_builder; @@ -94,7 +93,7 @@ impl Authz { pub(crate) async fn decode_tokens<'a>( &'a self, request: &'a Request, - ) -> Result, AuthorizeError> { + ) -> Result, JwtProcessingError> { let access = if let Some(tkn) = request.tokens.access_token.as_ref() { Some( self.config @@ -141,52 +140,74 @@ impl Authz { let schema = &self.config.policy_store.schema; - let tokens = self.decode_tokens(&request).await?; + let tokens = match self.decode_tokens(&request).await { + Ok(tokens) => tokens, + Err(e) => return Ok(AuthorizeResult::from(e)), + }; if let IdTokenTrustMode::Strict = self.config.authorization.id_token_trust_mode { - validate_id_tkn_trust_mode(&tokens)?; + if let Err(e) = validate_id_tkn_trust_mode(&tokens) { + return Ok(AuthorizeResult::from(e)); + }; } // Parse action UID. - let action = cedar_policy::EntityUid::from_str(request.action.as_str()) - .map_err(AuthorizeError::Action)?; + let action = match cedar_policy::EntityUid::from_str(request.action.as_str()) { + Ok(action) => action, + Err(e) => return Ok(AuthorizeResult::from(BadInputError::Action(e))), + }; // Parse [`cedar_policy::Entity`]-s to [`AuthorizeEntitiesData`] that hold all entities (for usability). - let entities_data = self + let entities_data = match self .entity_builder - .build_entities(&tokens, &request.resource)?; + .build_entities(&tokens, &request.resource) + { + Ok(entities) => entities, + Err(e) => return Ok(AuthorizeResult::from(e)), + }; // Get entity UIDs what we will be used on authorize check let resource_uid = entities_data.resource.uid(); - let context = build_context( + let context = match build_context( &self.config, request.context.clone(), &entities_data, &schema.schema, &action, - )?; + ) { + Ok(ctx) => ctx, + Err(e) => return Ok(AuthorizeResult::from(e)), + }; let workload_principal = entities_data.workload.as_ref().map(|e| e.uid()).to_owned(); let user_principal = entities_data.user.as_ref().map(|e| e.uid()).to_owned(); // Convert [`AuthorizeEntitiesData`] to [`cedar_policy::Entities`] structure, // hold all entities that will be used on authorize check. - let entities = entities_data.entities(Some(&schema.schema))?; + let entities = match entities_data.entities(Some(&schema.schema)) { + Ok(entities) => entities, + Err(e) => return Ok(AuthorizeResult::from(BadInputError::from(e))), + }; let (workload_authz_result, workload_authz_info, workload_entity_claims) = if let Some(workload) = workload_principal { let principal = workload; - let authz_result = self - .execute_authorize(ExecuteAuthorizeParameters { - entities: &entities, - principal: principal.clone(), - action: action.clone(), - resource: resource_uid.clone(), - context: context.clone(), - }) - .map_err(AuthorizeError::WorkloadRequestValidation)?; + let authz_result = match self.execute_authorize(ExecuteAuthorizeParameters { + entities: &entities, + principal: principal.clone(), + action: action.clone(), + resource: resource_uid.clone(), + context: context.clone(), + }) { + Ok(result) => result, + Err(e) => { + return Ok(AuthorizeResult::from( + BadInputError::WorkloadRequestValidation(e), + )); + }, + }; let authz_info = WorkloadAuthorizeInfo { principal: principal.to_string(), @@ -220,15 +241,20 @@ impl Authz { if let Some(user) = user_principal { let principal = user; - let authz_result = self - .execute_authorize(ExecuteAuthorizeParameters { - entities: &entities, - principal: principal.clone(), - action: action.clone(), - resource: resource_uid.clone(), - context: context.clone(), - }) - .map_err(AuthorizeError::UserRequestValidation)?; + let authz_result = match self.execute_authorize(ExecuteAuthorizeParameters { + entities: &entities, + principal: principal.clone(), + action: action.clone(), + resource: resource_uid.clone(), + context: context.clone(), + }) { + Ok(result) => result, + Err(e) => { + return Ok(AuthorizeResult::from(BadInputError::UserRequestValidation( + e, + ))); + }, + }; let authz_info = UserAuthorizeInfo { principal: principal.to_string(), @@ -268,93 +294,18 @@ impl Authz { .signed_duration_since(start_time) .num_milliseconds(); - // FROM THIS POINT WE ONLY MAKE LOGS - - // getting entities as json - let mut entities_raw_json = Vec::new(); - let cursor = Cursor::new(&mut entities_raw_json); - - entities.write_to_json(cursor)?; - let entities_json: serde_json::Value = serde_json::from_slice(entities_raw_json.as_slice()) - .map_err(AuthorizeError::EntitiesToJson)?; - - let user_authz_diagnostic = user_authz_info - .as_ref() - .map(|auth_info| &auth_info.diagnostics); - - let workload_authz_diagnostic = user_authz_info - .as_ref() - .map(|auth_info| &auth_info.diagnostics); - - let tokens_logging_info = LogTokensInfo { - access: tokens.access.as_ref().map(|tkn| { - tkn.logging_info( - self.config - .authorization - .decision_log_default_jwt_id - .as_str(), - ) - }), - id_token: tokens.id.as_ref().map(|tkn| { - tkn.logging_info( - self.config - .authorization - .decision_log_default_jwt_id - .as_str(), - ) - }), - userinfo: tokens.userinfo.as_ref().map(|tkn| { - tkn.logging_info( - self.config - .authorization - .decision_log_default_jwt_id - .as_str(), - ) - }), - }; - - // Decision log - // we log decision log before debug log, to avoid cloning diagnostic info - self.config.log_service.as_ref().log_any(&DecisionLogEntry { - base: BaseLogEntry::new(self.config.pdp_id, LogType::Decision), - policystore_id: self.config.policy_store.id.as_str(), - policystore_version: self.config.policy_store.get_store_version(), - principal: PrincipalLogEntry::new(&self.config.authorization), - user: user_entity_claims, - workload: workload_entity_claims, - lock_client_id: None, - action: request.action.clone(), - resource: resource_uid.to_string(), - decision: result.decision.into(), - tokens: tokens_logging_info, - decision_time_ms: elapsed_ms, - diagnostics: DiagnosticsRefs::new(&[ - &user_authz_diagnostic, - &workload_authz_diagnostic, - ]), - }); - - // DEBUG LOG - // Log all result information about both authorize checks. - // Where principal is `"Jans::Workload"` and where principal is `"Jans::User"`. - self.config.log_service.as_ref().log( - LogEntry::new_with_data( - self.config.pdp_id, - Some(self.config.application_name.clone()), - LogType::System, - ) - .set_level(LogLevel::DEBUG) - .set_auth_info(AuthorizationLogInfo { - action: request.action.clone(), - context: request.context.clone(), - resource: resource_uid.to_string(), - entities: entities_json, - person_authorize_info: user_authz_info, - workload_authorize_info: workload_authz_info, - authorized: result.decision, - }) - .set_message("Result of authorize.".to_string()), - ); + self.log_authz(LogAuthzArgs { + tokens, + entities: &entities, + user_authz_info, + workload_authz_info, + user_entity_claims, + workload_entity_claims, + request: &request, + resource: resource_uid, + result: &result, + elapsed_ms, + })?; Ok(result) } @@ -387,7 +338,7 @@ impl Authz { &self, request: &Request, tokens: &DecodedTokens<'_>, - ) -> Result { + ) -> Result { Ok(self .entity_builder .build_entities(tokens, &request.resource)?) @@ -472,6 +423,14 @@ impl AuthorizeEntitiesData { /// Error type for Authorization Service #[derive(thiserror::Error, Debug)] pub enum AuthorizeError { + /// Error encountered while logging the request + #[error("failed to log authorization request: {0}")] + Logging(#[from] LoggingError), +} + +/// Error returned because of invalid or malformed inputs to [`Authz::authorize`] +#[derive(thiserror::Error, Debug)] +pub enum BadInputError { /// Error encountered while processing JWT token data #[error(transparent)] ProcessTokens(#[from] jwt::JwtProcessingError), @@ -482,10 +441,10 @@ pub enum AuthorizeError { #[error("could not create context: {0}")] CreateContext(#[from] cedar_policy::ContextJsonError), /// Error encountered while creating [`cedar_policy::Request`] for workload entity principal - #[error("The request for `Workload` does not conform to the schema: {0}")] + #[error("the request for `Workload` does not conform to the schema: {0}")] WorkloadRequestValidation(cedar_policy::RequestValidationError), /// Error encountered while creating [`cedar_policy::Request`] for user entity principal - #[error("The request for `User` does not conform to the schema: {0}")] + #[error("the request for `User` does not conform to the schema: {0}")] UserRequestValidation(cedar_policy::RequestValidationError), /// Error encountered while collecting all entities #[error("could not collect all entities: {0}")] @@ -494,7 +453,7 @@ pub enum AuthorizeError { #[error("could convert entities to json: {0}")] EntitiesToJson(serde_json::Error), /// Error encountered while building the context for the request - #[error("Failed to build context: {0}")] + #[error("failed to build context: {0}")] BuildContext(#[from] BuildContextError), /// Error encountered while building the context for the request #[error("error while running on strict id token trust mode: {0}")] diff --git a/jans-cedarling/cedarling/src/lib.rs b/jans-cedarling/cedarling/src/lib.rs index 1059107ec22..b5c821c9241 100644 --- a/jans-cedarling/cedarling/src/lib.rs +++ b/jans-cedarling/cedarling/src/lib.rs @@ -35,7 +35,7 @@ use std::sync::Arc; use authz::AuthorizeEntitiesData; use authz::Authz; pub use authz::request::{Request, ResourceData, Tokens}; -pub use authz::{AuthorizeError, AuthorizeResult}; +pub use authz::{AuthorizeError, AuthorizeResult, BadInputError}; pub use bootstrap_config::*; use common::app_types; use init::ServiceFactory; @@ -136,7 +136,7 @@ impl Cedarling { pub async fn build_entities( &self, request: &Request, - ) -> Result { + ) -> Result { let tokens = self.authz.decode_tokens(request).await?; self.authz.build_entities(request, &tokens) } diff --git a/jans-cedarling/cedarling/src/log/log_entry.rs b/jans-cedarling/cedarling/src/log/log_entry.rs index 46add8d97fc..87e6ef157f0 100644 --- a/jans-cedarling/cedarling/src/log/log_entry.rs +++ b/jans-cedarling/cedarling/src/log/log_entry.rs @@ -6,17 +6,15 @@ //! # Log entry //! The module contains structs for logging events. -use std::collections::{HashMap, HashSet}; -use std::fmt::Display; -use std::hash::Hash; - -use uuid7::Uuid; - use super::LogLevel; use super::interface::Loggable; use crate::bootstrap_config::AuthorizationConfig; use crate::common::app_types::{self, ApplicationName}; use crate::common::policy_store::PoliciesContainer; +use std::collections::{HashMap, HashSet}; +use std::fmt::Display; +use std::hash::Hash; +use uuid7::Uuid; /// ISO-8601 time format for [`chrono`] /// example: 2024-11-27T10:10:50.654Z @@ -134,7 +132,6 @@ pub struct AuthorizationLogInfo { /// Workload authorize info #[serde(flatten)] pub workload_authorize_info: Option, - /// is authorized pub authorized: bool, } diff --git a/jans-cedarling/cedarling/src/log/mod.rs b/jans-cedarling/cedarling/src/log/mod.rs index 80747e88ad8..7ec9c2a02b3 100644 --- a/jans-cedarling/cedarling/src/log/mod.rs +++ b/jans-cedarling/cedarling/src/log/mod.rs @@ -49,26 +49,26 @@ //! //! Currently only [MemoryLogger](`memory_logger::MemoryLogger`) implement this. -pub mod interface; -mod log_entry; +#[cfg(test)] +mod test; + mod log_level; -pub(crate) mod log_strategy; mod memory_logger; mod nop_logger; mod stdout_logger; -pub use log_entry::*; -pub use log_level::*; - -#[cfg(test)] -mod test; +pub mod interface; -use std::sync::Arc; +pub(crate) mod log_entry; +pub(crate) mod log_strategy; +pub(crate) use log_strategy::LogStrategy; pub use interface::LogStorage; -pub(crate) use log_strategy::LogStrategy; +pub use log_entry::*; +pub use log_level::*; use crate::bootstrap_config::log_config::LogConfig; +use std::sync::Arc; /// Type alias for logger that is used in application pub(crate) type Logger = Arc; diff --git a/jans-cedarling/cedarling/src/tests/cases_authorize_different_principals.rs b/jans-cedarling/cedarling/src/tests/cases_authorize_different_principals.rs index c4b19069df3..8d7f2e942eb 100644 --- a/jans-cedarling/cedarling/src/tests/cases_authorize_different_principals.rs +++ b/jans-cedarling/cedarling/src/tests/cases_authorize_different_principals.rs @@ -83,15 +83,15 @@ async fn success_test_for_all_principals() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["2", "3"], - "reason of permit person should be '2'" + "reason of permit user should be '2'" ); assert!(result.decision, "request result should be allowed"); @@ -131,7 +131,7 @@ async fn success_test_for_principal_workload() { "reason of permit workload should be '1'" ); - assert!(result.person.is_none(), "result for person should be none"); + assert!(result.user.is_none(), "result for user should be none"); assert!(result.decision, "request result should be allowed"); } @@ -160,14 +160,14 @@ async fn success_test_for_principal_user() { .expect("request should be parsed without errors"); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["2"], - "reason of permit person should be '2'" + "reason of permit user should be '2'" ); assert!( @@ -178,10 +178,10 @@ async fn success_test_for_principal_user() { assert!(result.decision, "request result should be allowed"); } -/// Check if action executes for next principals: Person (only) +/// Check if action executes for next principals: User (only) /// check for user and role #[test] -async fn success_test_for_principal_person_role() { +async fn success_test_for_principal_user_role() { let cedarling = get_cedarling_with_authorization_conf( PolicyStoreSource::Yaml(POLICY_STORE_RAW_YAML.to_string()), crate::AuthorizationConfig { @@ -203,15 +203,15 @@ async fn success_test_for_principal_person_role() { .expect("request should be parsed without errors"); cmp_policy!( - result.person, + result.user, vec!["2", "3"], - "reason of permit person should be '2'" + "reason of permit user should be '2'" ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); assert!( @@ -222,7 +222,7 @@ async fn success_test_for_principal_person_role() { assert!(result.decision, "request result should be allowed"); } -/// Check if action executes for next principals: Workload AND Person (Role) +/// Check if action executes for next principals: Workload AND User (Role) #[test] async fn success_test_for_principal_workload_role() { let cedarling = get_cedarling_with_authorization_conf( @@ -257,20 +257,20 @@ async fn success_test_for_principal_workload_role() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["3"], - "reason of permit person should be '3'" + "reason of permit user should be '3'" ); assert!(result.decision, "request result should be allowed"); } -/// Check if action executes for next principals: Workload (true) OR Person (false) +/// Check if action executes for next principals: Workload (true) OR User (false) /// is used operator OR #[test] async fn success_test_for_principal_workload_true_or_user_false() { @@ -306,20 +306,20 @@ async fn success_test_for_principal_workload_true_or_user_false() { ); cmp_decision!( - result.person, + result.user, Decision::Deny, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, Vec::new() as Vec, - "reason of permit person should be empty" + "reason of permit user should be empty" ); assert!(result.decision, "request result should be allowed"); } -/// Check if action executes for next principals: Workload (false) OR Person (true) +/// Check if action executes for next principals: Workload (false) OR User (true) /// is used operator OR #[test] async fn success_test_for_principal_workload_false_or_user_true() { @@ -355,20 +355,20 @@ async fn success_test_for_principal_workload_false_or_user_true() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["2"], - "reason of permit person should be '2'" + "reason of permit user should be '2'" ); assert!(result.decision, "request result should be allowed"); } -/// Check if action executes for next principals: Workload (false) OR Person (false) +/// Check if action executes for next principals: Workload (false) OR User (false) /// is used operator OR #[test] async fn success_test_for_principal_workload_false_or_user_false() { @@ -404,14 +404,14 @@ async fn success_test_for_principal_workload_false_or_user_false() { ); cmp_decision!( - result.person, + result.user, Decision::Deny, - "request result should be not allowed for person" + "request result should be not allowed for user" ); cmp_policy!( - result.person, + result.user, Vec::new() as Vec, - "reason of permit person should be empty" + "reason of permit user should be empty" ); assert!(!result.decision, "request result should be not allowed"); @@ -438,11 +438,12 @@ async fn test_where_principal_workload_cant_be_applied() { let result = cedarling .authorize(request) .await - .expect_err("request should be parsed with error"); + .expect("request should be successful"); + assert_eq!(result.decision, false, "decision should be false"); assert!(matches!( - result, - crate::AuthorizeError::WorkloadRequestValidation(_) + result.reason_input, + Some(crate::BadInputError::WorkloadRequestValidation(_)) )) } @@ -467,11 +468,11 @@ async fn test_where_principal_user_cant_be_applied() { let result = cedarling .authorize(request) .await - .expect_err("request should be parsed with error"); + .expect("request should successful"); + assert_eq!(result.decision, false, "decision should be false"); - assert!( - matches!(result, crate::AuthorizeError::UserRequestValidation(_)), - "expected error UserRequestValidation, got: {}", - result - ) + assert!(matches!( + result.reason_input, + Some(crate::BadInputError::UserRequestValidation(_)) + ),) } diff --git a/jans-cedarling/cedarling/src/tests/cases_authorize_namespace_jans2.rs b/jans-cedarling/cedarling/src/tests/cases_authorize_namespace_jans2.rs index bfb69114ba3..9dacd2828a6 100644 --- a/jans-cedarling/cedarling/src/tests/cases_authorize_namespace_jans2.rs +++ b/jans-cedarling/cedarling/src/tests/cases_authorize_namespace_jans2.rs @@ -75,15 +75,15 @@ async fn test_namespace_jans2() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["2"], - "reason of permit person should be '2'" + "reason of permit user should be '2'" ); assert!(result.decision, "request result should be allowed"); diff --git a/jans-cedarling/cedarling/src/tests/cases_authorize_without_check_jwt.rs b/jans-cedarling/cedarling/src/tests/cases_authorize_without_check_jwt.rs index 53b9eba0b37..cdd2c509169 100644 --- a/jans-cedarling/cedarling/src/tests/cases_authorize_without_check_jwt.rs +++ b/jans-cedarling/cedarling/src/tests/cases_authorize_without_check_jwt.rs @@ -77,15 +77,15 @@ async fn success_test_role_string() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["2", "3"], - "reason of permit person should be '2','3'" + "reason of permit user should be '2','3'" ); assert!(result.decision, "request result should be allowed"); @@ -156,15 +156,15 @@ async fn forbid_test_role_guest() { ); cmp_decision!( - result.person, + result.user, Decision::Deny, - "request result should be not allowed for person with role Guest" + "request result should be not allowed for user with role Guest" ); cmp_policy!( - result.person, + result.user, vec!["4"], - "reason of permit person should be '2' and '4'" + "reason of permit user should be '2' and '4'" ); assert!(!result.decision, "request result should be not allowed"); @@ -236,15 +236,15 @@ async fn success_test_role_array() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["2", "3"], - "reason of permit person should be '2','3'" + "reason of permit user should be '2','3'" ); assert!(result.decision, "request result should be allowed"); @@ -317,15 +317,15 @@ async fn success_test_no_role() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["2"], - "reason of permit person should be '2'" + "reason of permit user should be '2'" ); assert!( @@ -399,15 +399,15 @@ async fn success_test_user_data_in_id_token() { ); cmp_decision!( - result.person, + result.workload, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["2", "3"], - "reason of permit person should be '2','3'" + "reason of permit user should be '2','3'" ); assert!(result.decision, "request result should be allowed"); @@ -478,15 +478,15 @@ async fn all_forbid() { ); cmp_decision!( - result.person, + result.user, Decision::Deny, - "request result should be forbidden for person with role Guest" + "request result should be forbidden for user with role Guest" ); cmp_policy!( - result.person, + result.user, vec!["4"], - "reason of forbid person should empty, no forbid rule" + "reason of forbid user should empty, no forbid rule" ); assert!(!result.decision, "request result should be not allowed"); @@ -555,21 +555,15 @@ async fn only_workload_permit() { ); cmp_decision!( - result.person, - Decision::Deny, - "request result should be forbidden for person" - ); - - cmp_decision!( - result.person, + result.user, Decision::Deny, - "request result should be forbidden for person" + "request result should be forbidden for user" ); cmp_policy!( - result.person, + result.user, vec!["4"], - "reason of forbid person should empty, no forbid rule" + "reason of forbid user should empty, no forbid rule" ); assert!(!result.decision, "request result should be not allowed"); @@ -639,16 +633,12 @@ async fn only_person_permit() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); - cmp_policy!( - result.person, - vec!["2"], - "reason of forbid person should '2'" - ); + cmp_policy!(result.user, vec!["2"], "reason of forbid user should '2'"); assert!(!result.decision, "request result should be not allowed"); } @@ -716,13 +706,13 @@ async fn only_user_role_permit() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be forbidden for person" + "request result should be forbidden for user" ); cmp_policy!( - result.person, + result.user, vec!["3"], "reason of forbid person '3', permit for role Admin" ); @@ -792,16 +782,12 @@ async fn only_workload_and_person_permit() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); - cmp_policy!( - result.person, - vec!["2"], - "reason of permit person should '2'" - ); + cmp_policy!(result.user, vec!["2"], "reason of permit user should '2'"); assert!(result.decision, "request result should be allowed"); } @@ -858,7 +844,7 @@ async fn only_workload_and_role_permit() { cmp_decision!( result.workload, Decision::Allow, - "request result should be allowed for workload" + "request result should be allowed for Workload" ); cmp_policy!( @@ -868,15 +854,15 @@ async fn only_workload_and_role_permit() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be not allowed for person" + "request result should be not allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["3"], - "reason of forbid person should be none, but we have permit for role" + "reason of forbid user should be none, but we have permit for role" ); assert!(result.decision, "request result should be allowed"); diff --git a/jans-cedarling/cedarling/src/tests/mapping_entities.rs b/jans-cedarling/cedarling/src/tests/mapping_entities.rs index 05dde523b33..9b9e8b79585 100644 --- a/jans-cedarling/cedarling/src/tests/mapping_entities.rs +++ b/jans-cedarling/cedarling/src/tests/mapping_entities.rs @@ -15,7 +15,7 @@ use crate::authz::entity_builder::{ BuildCedarlingEntityError, BuildEntityError, BuildTokenEntityError, }; use crate::common::policy_store::TokenKind; -use crate::{AuthorizeError, Cedarling, cmp_decision, cmp_policy}; +use crate::{BadInputError, Cedarling, cmp_decision, cmp_policy}; use cedarling_util::get_raw_config; use std::collections::HashSet; use std::sync::LazyLock; @@ -95,15 +95,15 @@ async fn test_default_mapping() { ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); cmp_policy!( - result.person, + result.user, vec!["2", "3"], - "reason of permit person should be '2','3'" + "reason of permit user should be '2','3'" ); assert!(result.decision, "request result should be allowed"); @@ -151,15 +151,15 @@ async fn test_custom_mapping() { ); cmp_policy!( - result.person, + result.user, vec!["5"], - "reason of permit person should be '5'" + "reason of permit user should be '5'" ); cmp_decision!( - result.person, + result.user, Decision::Allow, - "request result should be allowed for person" + "request result should be allowed for user" ); assert!(result.decision, "request result should be allowed"); @@ -182,13 +182,19 @@ async fn test_failed_user_mapping() { let request = REQUEST.clone(); - let err = cedarling + let result = cedarling .authorize(request) .await - .expect_err("request should be parsed with mapping error"); + .expect("request should be parsed successfully"); + assert_eq!(result.decision, false, "decision should be deny"); + + let err = result + .reason_input + .as_ref() + .expect("there should be an error due to the input"); match err { - AuthorizeError::BuildEntity(BuildCedarlingEntityError::User(error)) => { + BadInputError::BuildEntity(BuildCedarlingEntityError::User(error)) => { assert_eq!(error.errors.len(), 2, "there should be 2 errors"); let (token_kind, err) = &error.errors[0]; @@ -230,13 +236,18 @@ async fn test_failed_workload_mapping() { let request = REQUEST.clone(); - let err = cedarling + let result = cedarling .authorize(request) .await - .expect_err("request should be parsed with mapping error"); + .expect("request should be parsed successfully"); + assert_eq!(result.decision, false, "decision should be deny"); + let err = result + .reason_input + .as_ref() + .expect("there should be an error due to the input"); match err { - AuthorizeError::BuildEntity(BuildCedarlingEntityError::Workload(error)) => { + BadInputError::BuildEntity(BuildCedarlingEntityError::Workload(error)) => { assert_eq!(error.errors.len(), 2, "there should be 2 errors"); // check for access token error @@ -261,7 +272,7 @@ async fn test_failed_workload_mapping() { }, _ => panic!( "expected BuildEntity(BuildCedarlingEntityError::Workload(_))) error, got: {:?}", - err + result ), } } @@ -282,16 +293,22 @@ async fn test_failed_id_token_mapping() { let request = REQUEST.clone(); - let err = cedarling + let result = cedarling .authorize(request) .await - .expect_err("request should be parsed with mapping error"); + .expect("request should be parsed successfully"); + assert_eq!(result.decision, false, "decision should be deny"); + let err = result + .reason_input + .as_ref() + .expect("there should be an error due to the input"); match err { - AuthorizeError::BuildEntity(BuildCedarlingEntityError::IdToken( - BuildTokenEntityError { token_kind, err }, - )) => { - assert_eq!(token_kind, TokenKind::Id); + BadInputError::BuildEntity(BuildCedarlingEntityError::IdToken(BuildTokenEntityError { + token_kind, + err, + })) => { + assert_eq!(token_kind, &TokenKind::Id); assert!( matches!(err, BuildEntityError::EntityNotInSchema(ref name) if name == "MappedIdTokenNotExist"), "expected EntityNotInSchema(\"MappedIdTokenNotExist\") got: {:?}", @@ -321,16 +338,21 @@ async fn test_failed_access_token_mapping() { let request = REQUEST.clone(); - let err = cedarling + let result = cedarling .authorize(request) .await - .expect_err("request should be parsed with mapping error"); + .expect("request should be parsed successfully"); + assert_eq!(result.decision, false, "decision should be deny"); + let err = result + .reason_input + .as_ref() + .expect("there should be an error due to the input"); match err { - AuthorizeError::BuildEntity(BuildCedarlingEntityError::AccessToken( + BadInputError::BuildEntity(BuildCedarlingEntityError::AccessToken( BuildTokenEntityError { token_kind, err }, )) => { - assert_eq!(token_kind, TokenKind::Access); + assert_eq!(token_kind, &TokenKind::Access); assert!( matches!(err, BuildEntityError::EntityNotInSchema(ref name) if name == "MappedAccess_tokenNotExist"), "expected EntityNotInSchema(\"MappedAccess_tokenNotExist\") got: {:?}", @@ -357,16 +379,21 @@ async fn test_failed_userinfo_token_mapping() { let request = REQUEST.clone(); - let err = cedarling + let result = cedarling .authorize(request) .await - .expect_err("request should be parsed with mapping error"); + .expect("request should be parsed successfully"); + assert_eq!(result.decision, false, "decision should be deny"); + let err = result + .reason_input + .as_ref() + .expect("there should be an error due to the input"); match err { - AuthorizeError::BuildEntity(BuildCedarlingEntityError::UserinfoToken( + BadInputError::BuildEntity(BuildCedarlingEntityError::UserinfoToken( BuildTokenEntityError { token_kind, err }, )) => { - assert_eq!(token_kind, TokenKind::Userinfo); + assert_eq!(token_kind, &TokenKind::Userinfo); assert!( matches!(err, BuildEntityError::EntityNotInSchema(ref name) if name == "MappedUserinfo_tokenNotExist"), "expected EntityNotInSchema(\"MappedUserinfo_tokenNotExist\") got: {:?}", diff --git a/jans-cedarling/cedarling/src/tests/utils/cedarling_util.rs b/jans-cedarling/cedarling/src/tests/utils/cedarling_util.rs index aaf87e8edd5..f9b42b21248 100644 --- a/jans-cedarling/cedarling/src/tests/utils/cedarling_util.rs +++ b/jans-cedarling/cedarling/src/tests/utils/cedarling_util.rs @@ -4,7 +4,7 @@ // Copyright (c) 2024, Gluu, Inc. use crate::{ - authorization_config::IdTokenTrustMode, AuthorizationConfig, JwtConfig, WorkloadBoolOp, + AuthorizationConfig, JwtConfig, WorkloadBoolOp, authorization_config::IdTokenTrustMode, }; pub use crate::{ BootstrapConfig, BootstrapConfigRaw, Cedarling, FeatureToggle, LogConfig, LogTypeConfig,