summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/authorization/entities.rs21
-rw-r--r--src/authorization/gitlab_entities.rs208
-rw-r--r--src/gitlab/mod.rs2
-rw-r--r--src/gitlab/project.rs3
-rw-r--r--src/gitlab/user.rs13
5 files changed, 246 insertions, 1 deletions
diff --git a/src/authorization/entities.rs b/src/authorization/entities.rs
index fc1246d7..8d7d178f 100644
--- a/src/authorization/entities.rs
+++ b/src/authorization/entities.rs
@@ -46,6 +46,16 @@ impl EntitiesRepository {
let project = self.api.get_project(&project_path).await?;
+ // Create member list first
+ let member_ids: Vec<String> = self
+ .api
+ .get_project_members(project.id)
+ .await?
+ .iter()
+ .filter(|m| m.state == "active")
+ .map(|m| format!("User::\"{}\"", m.id))
+ .collect();
+
entities.push(CedarEntity {
uid: CedarUid {
entity_type: "Project".to_string(),
@@ -54,7 +64,10 @@ impl EntitiesRepository {
attrs: serde_json::json!({
"name": project.name,
"path": project.path,
- "full_path": format!("{}/{}", project.namespace.full_path, project.path),
+ "full_path": project.path_with_namespace,
+ "visibility": project.visibility,
+ "archived": project.archived.unwrap_or(false),
+ "members": member_ids,
}),
parents: if project.namespace.kind == "group" {
vec![CedarParent {
@@ -66,6 +79,7 @@ impl EntitiesRepository {
},
});
+ // Get all members again to create User entities
for member in self.api.get_project_members(project.id).await? {
if member.state == "active" {
entities.push(CedarEntity {
@@ -76,6 +90,9 @@ impl EntitiesRepository {
attrs: serde_json::json!({
"username": member.username,
"access_level": member.access_level,
+ "admin": false, // Would need to fetch from user API for real admin status
+ "blocked": false, // Would need to fetch from user API for real blocked status
+ "external": false, // Would need to fetch from user API for real external status
}),
parents: vec![],
});
@@ -133,6 +150,8 @@ impl EntitiesRepository {
"name": group.name,
"path": group.path,
"full_path": group.full_path,
+ "visibility": "private", // Groups don't have visibility in our simplified model
+ "members": [], // Would need group members API to populate
}),
parents,
});
diff --git a/src/authorization/gitlab_entities.rs b/src/authorization/gitlab_entities.rs
new file mode 100644
index 00000000..643f90e8
--- /dev/null
+++ b/src/authorization/gitlab_entities.rs
@@ -0,0 +1,208 @@
+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<CedarParent>,
+}
+
+#[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::<Vec<_>>(),
+ "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(),
+ }],
+ }
+ }
+} \ No newline at end of file
diff --git a/src/gitlab/mod.rs b/src/gitlab/mod.rs
index e1993d81..e8b49e0b 100644
--- a/src/gitlab/mod.rs
+++ b/src/gitlab/mod.rs
@@ -3,9 +3,11 @@ pub mod group;
pub mod member;
pub mod namespace;
pub mod project;
+pub mod user;
pub use api::Api;
pub use group::Group;
pub use member::Member;
pub use namespace::Namespace;
pub use project::Project;
+pub use user::User;
diff --git a/src/gitlab/project.rs b/src/gitlab/project.rs
index ba88c2e3..92201af3 100644
--- a/src/gitlab/project.rs
+++ b/src/gitlab/project.rs
@@ -7,5 +7,8 @@ pub struct Project {
pub id: u64,
pub name: String,
pub path: String,
+ pub path_with_namespace: String,
pub namespace: Namespace,
+ pub visibility: String, // "public", "internal", "private"
+ pub archived: Option<bool>,
}
diff --git a/src/gitlab/user.rs b/src/gitlab/user.rs
new file mode 100644
index 00000000..44ed61c1
--- /dev/null
+++ b/src/gitlab/user.rs
@@ -0,0 +1,13 @@
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize)]
+pub struct User {
+ pub id: u64,
+ pub username: String,
+ pub name: String,
+ pub state: String, // "active", "blocked", etc.
+ pub admin: Option<bool>,
+ pub bot: Option<bool>,
+ pub external: Option<bool>,
+ pub created_at: String,
+}