openauth-plugins 0.0.4

Official OpenAuth plugin modules.
Documentation
use std::collections::BTreeMap;

use crate::access::{
    create_access_control, request as access_request, role as access_role, statements,
    AccessControl, AccessError, Role as AccessRole, Statements,
};

use super::options::AdminOptions;

pub type PermissionMap = BTreeMap<String, Vec<String>>;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Role {
    access_role: AccessRole,
}

impl Role {
    pub fn new(permissions: PermissionMap) -> Self {
        let permissions = permissions
            .into_iter()
            .map(|(resource, actions)| (resource, actions.into_iter().collect()))
            .collect::<Statements>();
        let access_role = access_role(permissions);

        Self { access_role }
    }

    pub fn allows(&self, requested: &PermissionMap) -> bool {
        if requested.is_empty() {
            return true;
        }

        self.access_role
            .authorize_all(access_request(
                requested
                    .iter()
                    .map(|(resource, actions)| (resource.clone(), actions.clone())),
            ))
            .is_ok()
    }
}

pub fn has_permission(
    user_id: Option<&str>,
    role: Option<&str>,
    options: &AdminOptions,
    permissions: &PermissionMap,
) -> bool {
    if user_id.is_some_and(|id| options.admin_user_ids.iter().any(|admin_id| admin_id == id)) {
        return true;
    }

    let role = role.unwrap_or(&options.default_role);
    for role_name in role
        .split(',')
        .map(str::trim)
        .filter(|item| !item.is_empty())
    {
        if options
            .roles
            .get(role_name)
            .is_some_and(|role| role.allows(permissions))
        {
            return true;
        }
    }
    false
}

pub fn default_roles() -> BTreeMap<String, Role> {
    let mut roles = BTreeMap::new();
    roles.insert("admin".to_owned(), admin_role());
    roles.insert("user".to_owned(), Role::new(PermissionMap::new()));
    roles
}

pub fn default_statements() -> Statements {
    statements([
        (
            "user",
            vec![
                "create",
                "list",
                "set-role",
                "ban",
                "impersonate",
                "impersonate-admins",
                "delete",
                "set-password",
                "get",
                "update",
            ],
        ),
        ("session", vec!["list", "revoke", "delete"]),
    ])
}

pub fn default_access_control() -> Result<AccessControl, AccessError> {
    create_access_control(default_statements())
}

fn admin_role() -> Role {
    Role::new(PermissionMap::from([
        (
            "user".to_owned(),
            vec![
                "create".to_owned(),
                "list".to_owned(),
                "set-role".to_owned(),
                "ban".to_owned(),
                "impersonate".to_owned(),
                "delete".to_owned(),
                "set-password".to_owned(),
                "get".to_owned(),
                "update".to_owned(),
            ],
        ),
        (
            "session".to_owned(),
            vec!["list".to_owned(), "revoke".to_owned(), "delete".to_owned()],
        ),
    ]))
}