coil-auth 0.1.0

Authorisation models and auth package support for the Coil framework.
Documentation
use super::*;

pub fn default_schema() -> Schema {
    SchemaBuilder::new()
        .namespace(Namespace::Tenant.as_str(), top_level_namespace())
        .namespace(
            Namespace::Site.as_str(),
            inherited_namespace(Relation::Tenant),
        )
        .namespace(
            Namespace::Brand.as_str(),
            inherited_namespace(Relation::Site),
        )
        .namespace(
            Namespace::Storefront.as_str(),
            storefront_namespace(Relation::Brand),
        )
        .namespace(Namespace::Group.as_str(), principal_set_namespace())
        .namespace(Namespace::Team.as_str(), principal_set_namespace())
        .namespace(
            Namespace::Page.as_str(),
            inherited_namespace(Relation::Site),
        )
        .namespace(
            Namespace::Navigation.as_str(),
            inherited_namespace(Relation::Site),
        )
        .namespace(
            Namespace::Product.as_str(),
            inherited_namespace(Relation::Storefront),
        )
        .namespace(
            Namespace::Collection.as_str(),
            inherited_namespace(Relation::Storefront),
        )
        .namespace(
            Namespace::Order.as_str(),
            order_namespace(Relation::Storefront),
        )
        .namespace(
            Namespace::Subscription.as_str(),
            inherited_namespace(Relation::Storefront),
        )
        .namespace(
            Namespace::MembershipTier.as_str(),
            inherited_namespace(Relation::Storefront),
        )
        .namespace(
            Namespace::Event.as_str(),
            inherited_namespace(Relation::Site),
        )
        .namespace(
            Namespace::EventSlot.as_str(),
            event_slot_namespace(Relation::Event),
        )
        .namespace(
            Namespace::Booking.as_str(),
            booking_namespace(Relation::Slot),
        )
        .namespace(
            Namespace::MediaLibrary.as_str(),
            inherited_namespace(Relation::Site),
        )
        .namespace(
            Namespace::Media.as_str(),
            media_namespace(Relation::Library),
        )
        .namespace(
            Namespace::AssetFolder.as_str(),
            inherited_namespace(Relation::Site),
        )
        .namespace(Namespace::Asset.as_str(), asset_namespace(Relation::Folder))
        .namespace(
            Namespace::ThemeAssetBundle.as_str(),
            inherited_namespace(Relation::Site),
        )
        .namespace(
            Namespace::AdminModule.as_str(),
            admin_module_namespace(Relation::Site),
        )
        .build()
}

fn top_level_namespace() -> NamespaceConfig {
    NamespaceConfig {
        rules: permission_ladder(),
    }
}

fn principal_set_namespace() -> NamespaceConfig {
    let mut rules = permission_ladder();
    rules.insert(
        Relation::View.to_string(),
        vec![
            inherit(Relation::Member),
            inherit(Relation::Viewer),
            inherit(Relation::Support),
            inherit(Relation::Edit),
        ],
    );
    NamespaceConfig { rules }
}

fn inherited_namespace(link_relation: Relation) -> NamespaceConfig {
    let mut rules = permission_ladder();
    add_inherited_roles(&mut rules, link_relation);
    NamespaceConfig { rules }
}

fn storefront_namespace(link_relation: Relation) -> NamespaceConfig {
    let mut rules = permission_ladder();
    add_inherited_roles(&mut rules, link_relation);
    rules.insert(
        Relation::Checkout.to_string(),
        vec![inherit(Relation::View), inherit(Relation::Member)],
    );
    NamespaceConfig { rules }
}

fn order_namespace(link_relation: Relation) -> NamespaceConfig {
    let mut rules = permission_ladder();
    add_inherited_roles(&mut rules, link_relation);
    rules.insert(
        Relation::Refund.to_string(),
        vec![inherit(Relation::Manage), inherit(Relation::Support)],
    );
    NamespaceConfig { rules }
}

fn event_slot_namespace(link_relation: Relation) -> NamespaceConfig {
    let mut rules = permission_ladder();
    add_inherited_roles(&mut rules, link_relation);
    rules.insert(
        Relation::Book.to_string(),
        vec![inherit(Relation::View), inherit(Relation::Member)],
    );
    NamespaceConfig { rules }
}

fn booking_namespace(link_relation: Relation) -> NamespaceConfig {
    let mut rules = permission_ladder();
    add_inherited_roles(&mut rules, link_relation);
    rules.insert(
        Relation::CheckIn.to_string(),
        vec![inherit(Relation::Manage), inherit(Relation::Support)],
    );
    NamespaceConfig { rules }
}

fn media_namespace(link_relation: Relation) -> NamespaceConfig {
    let mut rules = permission_ladder();
    add_inherited_roles(&mut rules, link_relation);
    rules.insert(Relation::Read.to_string(), vec![inherit(Relation::View)]);
    NamespaceConfig { rules }
}

fn admin_module_namespace(link_relation: Relation) -> NamespaceConfig {
    let mut rules = permission_ladder();
    add_inherited_roles(&mut rules, link_relation);
    rules.insert(Relation::Read.to_string(), vec![inherit(Relation::View)]);
    NamespaceConfig { rules }
}

fn asset_namespace(link_relation: Relation) -> NamespaceConfig {
    let mut rules = permission_ladder();
    add_inherited_roles(&mut rules, link_relation);
    rules.insert(Relation::Read.to_string(), vec![inherit(Relation::View)]);
    rules.insert(Relation::Replace.to_string(), vec![inherit(Relation::Edit)]);
    rules.insert(
        Relation::Delete.to_string(),
        vec![inherit(Relation::Manage)],
    );
    rules.insert(
        Relation::Unpublish.to_string(),
        vec![inherit(Relation::Manage)],
    );
    rules.insert(
        Relation::ManageStorage.to_string(),
        vec![inherit(Relation::Manage)],
    );
    NamespaceConfig { rules }
}

fn permission_ladder() -> HashMap<String, Vec<RelationRule>> {
    HashMap::from([
        (
            Relation::Manage.to_string(),
            vec![inherit(Relation::Owner), inherit(Relation::Admin)],
        ),
        (
            Relation::Publish.to_string(),
            vec![inherit(Relation::Manage), inherit(Relation::Editor)],
        ),
        (
            Relation::Edit.to_string(),
            vec![inherit(Relation::Publish), inherit(Relation::Editor)],
        ),
        (
            Relation::View.to_string(),
            vec![
                inherit(Relation::Edit),
                inherit(Relation::Viewer),
                inherit(Relation::Support),
            ],
        ),
    ])
}

fn add_inherited_roles(rules: &mut HashMap<String, Vec<RelationRule>>, link_relation: Relation) {
    for relation in [
        Relation::Member,
        Relation::Owner,
        Relation::Admin,
        Relation::Editor,
        Relation::Viewer,
        Relation::Support,
    ] {
        rules.insert(
            relation.to_string(),
            vec![computed(link_relation, relation)],
        );
    }
}

fn inherit(relation: Relation) -> RelationRule {
    RelationRule::Inherit(relation.to_string())
}

fn computed(tuple_relation: Relation, target_relation: Relation) -> RelationRule {
    RelationRule::Computed {
        tuple_relation: tuple_relation.to_string(),
        target_relation: target_relation.to_string(),
    }
}