cedarling 0.0.65

The Cedarling: a high-performance local authorization service powered by the Rust Cedar Engine.
Documentation
// 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::{
    BuildEntityError, BuildEntityErrorKind, BuiltEntities, Entity, EntityBuilder, HashSet,
    build_cedar_entity, build_cedar_uid, build_entity_attrs,
};
use crate::EntityData;

impl EntityBuilder {
    pub(crate) fn build_resource_entity(
        &self,
        resource_data: &EntityData,
    ) -> Result<Entity, BuildEntityError> {
        let resource_type_name = &resource_data.cedar_mapping.entity_type;

        // Check for default entity first when attributes are empty
        if resource_data.attributes.is_empty() {
            let uid = build_cedar_uid(resource_type_name, &resource_data.cedar_mapping.id)?;
            if let Some(resource_default_entity) = self.default_entities.get(&uid) {
                return Ok(resource_default_entity.clone());
            }
        }

        let attrs_shape = self
            .schema
            .as_ref()
            .and_then(|s| s.get_entity_shape(resource_type_name));
        let attrs = build_entity_attrs(
            &resource_data.attributes,
            &BuiltEntities::default(),
            attrs_shape,
        )
        .map_err(|e| BuildEntityErrorKind::from(e).while_building(resource_type_name))?;

        let resource = build_cedar_entity(
            resource_type_name,
            &resource_data.cedar_mapping.id,
            attrs,
            HashSet::new(),
        )?;

        Ok(resource)
    }
}

#[cfg(test)]
mod test {
    use super::super::test::*;
    use super::super::*;
    use crate::CedarEntityMapping;
    use serde_json::json;

    #[test]
    fn can_build_entity() {
        let builder = EntityBuilder::new(
            TrustedIssuerIndex::new(&HashMap::new(), None),
            Some(&CEDARLING_VALIDATOR_SCHEMA),
            DefaultEntities::default(),
        )
        .expect("should init entity builder");
        let resource_data = EntityData {
            cedar_mapping: CedarEntityMapping {
                entity_type: "Jans::HTTP_Request".to_string(),
                id: "some_request".to_string(),
            },
            attributes: HashMap::from([
                ("header".to_string(), json!({"Accept": "test"})),
                (
                    "url".to_string(),
                    json!({"host": "protected.host", "protocol": "http", "path": "/protected"}),
                ),
            ]),
        };
        let entity = builder
            .build_resource_entity(&resource_data)
            .expect("expected to build resource entity");

        assert_entity_eq(
            &entity,
            &json!({
                "uid": {"type": "Jans::HTTP_Request", "id": "some_request"},
                "attrs": {
                    "url": {
                        "host": "protected.host",
                        "protocol": "http",
                        "path": "/protected",
                    },
                    "header": {
                        "Accept": "test",
                    }
                },
                "parents": [],
            }),
            Some(&CEDARLING_API_SCHEMA),
        );
    }

    #[test]
    fn can_build_entity_with_optional_attr() {
        let builder = EntityBuilder::new(
            TrustedIssuerIndex::new(&HashMap::new(), None),
            Some(&CEDARLING_VALIDATOR_SCHEMA),
            DefaultEntities::default(),
        )
        .expect("should init entity builder");
        // Provide required attributes but omit the optional "Accept" header
        let resource_data = EntityData {
            cedar_mapping: CedarEntityMapping {
                entity_type: "Jans::HTTP_Request".to_string(),
                id: "some_request".to_string(),
            },
            attributes: HashMap::from([
                (
                    "url".to_string(),
                    json!({"host": "protected.host", "protocol": "http", "path": "/protected"}),
                ),
                ("header".to_string(), json!({})),
            ]),
        };
        let entity = builder
            .build_resource_entity(&resource_data)
            .expect("expected to build resource entity");

        assert!(
            entity.attr("url").is_some(),
            "entity should have a `url` attribute"
        );
        assert!(
            entity.attr("header").is_some(),
            "entity should have a `header` attribute"
        );
    }
}