Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jans-cedarling): Implement check authorization principals based on the schema for action #10126

Merged
merged 19 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cd5ffd6
chore(jans-cedarling): move `cedar_schema.rs` file to `mod.rs`
olehbozhok Nov 7, 2024
277edc1
Merge commit '535520d8a285f26c96640c310f4ef3955a669190' into jans-ced…
olehbozhok Nov 7, 2024
6f91de7
chore(jans-cedarling): refactor move types to parse cedar-policy enti…
olehbozhok Nov 7, 2024
df540e1
feat(jans-cedarling): add parsing actions from json cedar-policy schema
olehbozhok Nov 8, 2024
f482d21
chore(jans-cedarling): update `AuthorizationLogInfo` to be more flexi…
olehbozhok Nov 11, 2024
606be23
Merge commit '7083a5e4464b870b8dc0b82e227c0b9065591cc8' into jans-ced…
olehbozhok Nov 11, 2024
30fda48
chore(jans-cedarling): add update to `AuthorizationLogInfo` structure…
olehbozhok Nov 11, 2024
e1771f7
feat(jans-cedarling): add authorize check only for defined `principal…
olehbozhok Nov 12, 2024
8697cf1
chore(jans-cedarling): refactor test/utils module, split functions to…
olehbozhok Nov 12, 2024
5b88d0c
chore(jans-cedarling): move util macros to util test util crate
olehbozhok Nov 12, 2024
c848622
test(jans-cedarling): add test check when different principal can be …
olehbozhok Nov 12, 2024
ef16a33
chore(jans-cedarling): implement more easier implementation to apply …
olehbozhok Nov 12, 2024
ea07170
feat(jans-cedarling): add loading namespace from policy store
olehbozhok Nov 12, 2024
3b7882a
chore(jans-cedarling): fix test cases after changes
olehbozhok Nov 12, 2024
a8224a6
test(jans-cedarling): add test case to check if namespace different f…
olehbozhok Nov 12, 2024
89293cb
chore(jans-cedarling): fix clippy issues
olehbozhok Nov 12, 2024
6caee4b
chore(jans-cedarling): add better comment to macros
olehbozhok Nov 13, 2024
84761ba
Merge commit '9801df403cba4a2d0935f58786c007327cd4cbac' into jans-ced…
olehbozhok Nov 13, 2024
5d93ecc
Merge branch 'main' into jans-cedaling-issue-10072
olehbozhok Nov 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions jans-cedarling/bindings/cedarling_python/cedarling_python.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class DisabledLoggingConfig:

@final
class PolicyStoreSource:
def __init__(self, json: Optional[str] = None, yaml: Optional[str] = None) -> None: ...
def __init__(self, json: Optional[str] = None,
yaml: Optional[str] = None) -> None: ...


@final
Expand Down Expand Up @@ -107,9 +108,9 @@ class ResourceData:
class AuthorizeResult:
def is_allowed(self) -> bool: ...

def workload(self) -> AuthorizeResultResponse: ...
def workload(self) -> AuthorizeResultResponse | None: ...

def person(self) -> AuthorizeResultResponse: ...
def person(self) -> AuthorizeResultResponse | None: ...

def role(self) -> AuthorizeResultResponse | None: ...

Expand Down
35 changes: 19 additions & 16 deletions jans-cedarling/bindings/cedarling_python/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,29 +196,32 @@
workload_result = authorize_result.workload()
print(f"Result of workload authorization: {workload_result.decision}")

# show diagnostic information
workload_diagnostic = workload_result.diagnostics
print("Policy ID(s) used:")
for diagnostic in workload_diagnostic.reason:
print(diagnostic)
if workload_result is not None:
# show diagnostic information
workload_diagnostic = workload_result.diagnostics
print("Policy ID(s) used:")
for diagnostic in workload_diagnostic.reason:
print(diagnostic)

print("Errors during authorization:")
for diagnostic in workload_diagnostic.errors:
print(diagnostic)
print("Errors during authorization:")
for diagnostic in workload_diagnostic.errors:
print(diagnostic)

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
print("Policy ID(s) used:")
for diagnostic in person_diagnostic.reason:
print(diagnostic)

print("Errors during authorization:")
for diagnostic in person_diagnostic.errors:
print(diagnostic)

if person_result is not None:
person_diagnostic = person_result.diagnostics
print("Policy ID(s) used:")
for diagnostic in person_diagnostic.reason:
print(diagnostic)

print("Errors during authorization:")
for diagnostic in person_diagnostic.errors:
print(diagnostic)


# watch on the decision for role if present
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ impl AuthorizeResult {
}

/// Get the decision value for workload
fn workload(&self) -> AuthorizeResultResponse {
self.inner.workload.clone().into()
fn workload(&self) -> Option<AuthorizeResultResponse> {
self.inner.workload.clone().map(|v| v.into())
}

/// Get the decision value for person/user
fn person(&self) -> AuthorizeResultResponse {
self.inner.person.clone().into()
fn person(&self) -> Option<AuthorizeResultResponse> {
self.inner.person.clone().map(|v| v.into())
}

/// Get the decision value for role
Expand Down
35 changes: 25 additions & 10 deletions jans-cedarling/cedarling/src/authz/authorize_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use cedar_policy::Decision;
/// based on the [Request](crate::models::request::Request) and policy store
pub struct AuthorizeResult {
/// Result of authorization where principal is `Jans::Workload`
pub workload: cedar_policy::Response,
pub workload: Option<cedar_policy::Response>,
/// Result of authorization where principal is `Jans::User`
pub person: cedar_policy::Response,
pub person: Option<cedar_policy::Response>,
/// Result of authorization where principal is `Jans::Role`
pub role: Option<cedar_policy::Response>,
}
Expand All @@ -22,16 +22,31 @@ impl AuthorizeResult {
/// `workload` (i.e., primary principal) needs to permit the request and
/// additional conditions (either `person` or `role`) must also indicate allowance.
pub fn is_allowed(&self) -> bool {
let role_decision = self
.role
let workload_allowed = self
.workload
.as_ref()
.map(|response| response.decision() == Decision::Allow);

let person_allowed = self
.person
.as_ref()
.map(|result| result.decision())
.unwrap_or(Decision::Deny);
.map(|response| response.decision() == Decision::Allow);

let workload_allowed = self.workload.decision() == Decision::Allow;
let person_or_role_allowed =
self.person.decision() == Decision::Allow || role_decision == Decision::Allow;
let role_allowed = self
.role
.as_ref()
.map(|response| response.decision() == Decision::Allow);

workload_allowed && person_or_role_allowed
// cover each possible case when any of value is Some or None
match (workload_allowed, person_allowed, role_allowed) {
(None, None, None) => false,
(None, None, Some(role)) => role,
(None, Some(person), None) => person,
(None, Some(person), Some(role)) => person || role,
(Some(workload), None, None) => workload,
(Some(workload), None, Some(role)) => workload && role,
(Some(workload), Some(person), None) => workload && person,
(Some(workload), Some(person), Some(role)) => workload && (person || role),
}
}
}
61 changes: 32 additions & 29 deletions jans-cedarling/cedarling/src/authz/entities/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,28 @@ use std::{
str::FromStr,
};

use crate::authz::token_data::{GetTokenClaimValue, Payload, TokenPayload};
use crate::common::cedar_schema::{
cedar_json::{CedarSchemaRecord, CedarType, GetCedarTypeError},
cedar_json::{CedarSchemaEntityShape, CedarSchemaRecord, CedarType, GetCedarTypeError},
CedarSchemaJson,
};
use crate::{
authz::token_data::{GetTokenClaimValue, Payload, TokenPayload},
common::cedar_schema::cedar_json::CedarSchemaEntityShape,
};

use cedar_policy::{EntityId, EntityTypeName, EntityUid, RestrictedExpression};

use super::trait_as_expression::AsExpression;

const CEDAR_POLICY_SEPARATOR: &str = "::";
pub const CEDAR_POLICY_SEPARATOR: &str = "::";

/// Meta information about an entity type.
/// Is used to store in `static` variable.
pub(crate) struct EntityMetadata<'a> {
pub entity_type: &'a str,
pub entity_type: EntityParsedTypeName<'a>,
pub entity_id_data_key: &'a str,
}

impl<'a> EntityMetadata<'a> {
/// create new instance of EntityMetadata.
pub fn new(entity_type: &'a str, entity_id_data_key: &'a str) -> Self {
pub fn new(entity_type: EntityParsedTypeName<'a>, entity_id_data_key: &'a str) -> Self {
Self {
entity_type,
entity_id_data_key,
Expand All @@ -51,12 +48,11 @@ impl<'a> EntityMetadata<'a> {
parents: HashSet<EntityUid>,
) -> Result<cedar_policy::Entity, CedarPolicyCreateTypeError> {
let entity_uid = build_entity_uid(
self.entity_type,
self.entity_type.full_type_name().as_str(),
data.get_payload(self.entity_id_data_key)?.as_str()?,
)?;

let parsed_typename = parse_namespace_and_typename(self.entity_type);
create_entity(entity_uid, &parsed_typename, schema, data, parents)
create_entity(entity_uid, &self.entity_type, schema, data, parents)
}
}

Expand All @@ -79,42 +75,51 @@ pub(crate) fn build_entity_uid(
/// Analog to the internal cedar_policy type `InternalName`
pub(crate) struct EntityParsedTypeName<'a> {
pub typename: &'a str,
path: Vec<&'a str>,
pub namespace: &'a str,
}
impl<'a> EntityParsedTypeName<'a> {
pub fn namespace(&self) -> String {
self.path.join(CEDAR_POLICY_SEPARATOR)
pub fn new(typename: &'a str, namespace: &'a str) -> Self {
EntityParsedTypeName {
typename,
namespace,
}
}

pub fn full_type_name(&self) -> String {
if self.namespace.is_empty() {
self.typename.to_string()
} else {
[self.namespace, self.typename].join(CEDAR_POLICY_SEPARATOR)
}
}
}

/// Parse entity type name and namespace from entity type string.
pub fn parse_namespace_and_typename(entity_type: &str) -> EntityParsedTypeName {
let mut raw_path: Vec<&str> = entity_type.split(CEDAR_POLICY_SEPARATOR).collect();
/// return (typename, namespace)
pub fn parse_namespace_and_typename(raw_entity_type: &str) -> (&str, String) {
let mut raw_path: Vec<&str> = raw_entity_type.split(CEDAR_POLICY_SEPARATOR).collect();
let typename = raw_path.pop().unwrap_or_default();
EntityParsedTypeName {
typename,
path: raw_path,
}
let namespace = raw_path.join(CEDAR_POLICY_SEPARATOR);
(typename, namespace)
}

/// fetch the schema record for a given entity type from the cedar schema json
fn fetch_schema_record<'a>(
entity_namespace: &str,
entity_typename: &str,
entity_info: &EntityParsedTypeName,
schema: &'a CedarSchemaJson,
) -> Result<&'a CedarSchemaEntityShape, CedarPolicyCreateTypeError> {
let entity_shape = schema
.entity_schema(entity_namespace, entity_typename)
.entity_schema(entity_info.namespace, entity_info.typename)
.ok_or(CedarPolicyCreateTypeError::CouldNotFindEntity(
entity_typename.to_string(),
entity_info.typename.to_string(),
))?;

// just to check if the entity is a record to be sure
// if shape not empty
if let Some(entity_record) = &entity_shape.shape {
if !entity_record.is_record() {
return Err(CedarPolicyCreateTypeError::NotRecord(
entity_typename.to_string(),
entity_info.typename.to_string(),
));
};
}
Expand Down Expand Up @@ -180,12 +185,10 @@ pub fn create_entity<'a>(
data: &'a TokenPayload,
parents: HashSet<EntityUid>,
) -> Result<cedar_policy::Entity, CedarPolicyCreateTypeError> {
let entity_namespace = parsed_typename.namespace();

// fetch the schema entity shape from the json-schema.
let schema_shape = fetch_schema_record(&entity_namespace, parsed_typename.typename, schema)?;
let schema_shape = fetch_schema_record(parsed_typename, schema)?;

let attrs = build_entity_attributes(schema_shape, data, &entity_namespace)?;
let attrs = build_entity_attributes(schema_shape, data, parsed_typename.namespace)?;

let entity_uid_string = entity_uid.to_string();
cedar_policy::Entity::new(entity_uid, attrs, parents)
Expand Down
21 changes: 0 additions & 21 deletions jans-cedarling/cedarling/src/authz/entities/meta.rs

This file was deleted.

Loading