use serde::Serialize; use std::collections::HashMap; // Cedar entity structures optimized for GitLab authorization #[derive(Debug, Serialize)] pub struct CedarEntity { pub uid: CedarUid, pub attrs: serde_json::Value, pub parents: Vec, } #[derive(Debug, Serialize)] pub struct CedarUid { #[serde(rename = "type")] pub entity_type: String, pub id: String, } #[derive(Debug, Serialize)] pub struct CedarParent { #[serde(rename = "type")] pub parent_type: String, pub id: String, } // GitLab-specific entity builders pub struct GitLabEntityBuilder; impl GitLabEntityBuilder { // Build User entity with GitLab-specific attributes pub fn build_user(user: &crate::gitlab::User) -> CedarEntity { CedarEntity { uid: CedarUid { entity_type: "User".to_string(), id: user.id.to_string(), }, attrs: serde_json::json!({ "username": user.username, "name": user.name, "admin": user.admin.unwrap_or(false), "blocked": user.state == "blocked", "bot": user.bot.unwrap_or(false), "external": user.external.unwrap_or(false), "created_at": user.created_at }), parents: vec![], } } // Build Project entity - simplified without feature checks pub fn build_project(project: &crate::gitlab::Project) -> CedarEntity { CedarEntity { uid: CedarUid { entity_type: "Project".to_string(), id: project.id.to_string(), }, attrs: serde_json::json!({ "name": project.name, "path": project.path, "full_path": project.path_with_namespace, "visibility": project.visibility, "archived": project.archived.unwrap_or(false), "members": [] // Will be populated separately }), parents: vec![CedarParent { parent_type: "Namespace".to_string(), id: project.namespace.id.to_string(), }], } } // Build Group/Namespace entity pub fn build_namespace(namespace: &crate::gitlab::Namespace) -> CedarEntity { let mut parents = vec![]; if let Some(parent_id) = namespace.parent_id { parents.push(CedarParent { parent_type: "Namespace".to_string(), id: parent_id.to_string(), }); } CedarEntity { uid: CedarUid { entity_type: "Namespace".to_string(), id: namespace.id.to_string(), }, attrs: serde_json::json!({ "name": namespace.name, "path": namespace.path, "full_path": namespace.full_path, "kind": namespace.kind, "visibility_level": namespace.visibility_level }), parents, } } // Build Membership entity - represents user access to projects/groups pub fn build_project_membership( user_id: u64, project_id: u64, member: &crate::gitlab::Member, ) -> CedarEntity { CedarEntity { uid: CedarUid { entity_type: "ProjectMembership".to_string(), id: format!("{}:{}", user_id, project_id), }, attrs: serde_json::json!({ "user_id": user_id, "project_id": project_id, "access_level": member.access_level, "expires_at": member.expires_at }), parents: vec![ CedarParent { parent_type: "User".to_string(), id: user_id.to_string(), }, CedarParent { parent_type: "Project".to_string(), id: project_id.to_string(), }, ], } } // Build Group Membership entity pub fn build_group_membership( user_id: u64, group_id: u64, member: &crate::gitlab::Member, ) -> CedarEntity { CedarEntity { uid: CedarUid { entity_type: "GroupMembership".to_string(), id: format!("{}:{}", user_id, group_id), }, attrs: serde_json::json!({ "user_id": user_id, "group_id": group_id, "access_level": member.access_level, "expires_at": member.expires_at }), parents: vec![ CedarParent { parent_type: "User".to_string(), id: user_id.to_string(), }, CedarParent { parent_type: "Namespace".to_string(), id: group_id.to_string(), }, ], } } // Build Issue entity pub fn build_issue(issue: &crate::gitlab::Issue, project_id: u64) -> CedarEntity { CedarEntity { uid: CedarUid { entity_type: "Issue".to_string(), id: issue.id.to_string(), }, attrs: serde_json::json!({ "iid": issue.iid, "title": issue.title, "state": issue.state, "confidential": issue.confidential, "author_id": issue.author.id, "assignee_ids": issue.assignees.iter().map(|a| a.id).collect::>(), "created_at": issue.created_at, "updated_at": issue.updated_at }), parents: vec![CedarParent { parent_type: "Project".to_string(), id: project_id.to_string(), }], } } // Build MergeRequest entity pub fn build_merge_request(mr: &crate::gitlab::MergeRequest, project_id: u64) -> CedarEntity { CedarEntity { uid: CedarUid { entity_type: "MergeRequest".to_string(), id: mr.id.to_string(), }, attrs: serde_json::json!({ "iid": mr.iid, "title": mr.title, "state": mr.state, "merge_status": mr.merge_status, "author_id": mr.author.id, "assignee_id": mr.assignee.as_ref().map(|a| a.id), "target_branch": mr.target_branch, "source_branch": mr.source_branch, "work_in_progress": mr.work_in_progress, "created_at": mr.created_at, "updated_at": mr.updated_at }), parents: vec![CedarParent { parent_type: "Project".to_string(), id: project_id.to_string(), }], } } }