Skip to content
Clone
ef0b83a
274 lines 8.9 KB Rust
use models::repo::repo_branch_protection_whitelists::{self, WhitelistKind};
use models::repo::{
repo_branch_protection_policy, repo_tag_protection_policy, repo_tag_protection_whitelists,
};
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set, TransactionTrait};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use utoipa::ToSchema;
use uuid::Uuid;
use crate::service::GitService;
use crate::share::errors::GitError;
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct BranchWhitelistDto {
pub id: Uuid,
pub policy_id: Uuid,
pub kind: WhitelistKind,
pub user_id: Option<i64>,
pub user: Option<space::services::dto::UserDto>,
pub group_id: Option<Uuid>,
pub created_at: OffsetDateTime,
}
impl From<repo_branch_protection_whitelists::Model> for BranchWhitelistDto {
fn from(m: repo_branch_protection_whitelists::Model) -> Self {
Self {
id: m.id,
policy_id: m.policy_id,
kind: m.kind,
user_id: m.user_id,
user: None,
group_id: m.group_id,
created_at: OffsetDateTime::now_utc(),
}
}
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct AddBranchWhitelistCmd {
pub kind: WhitelistKind,
pub user_id: Option<i64>,
pub group_id: Option<Uuid>,
}
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct TagWhitelistDto {
pub policy_id: Uuid,
pub user_id: i64,
pub user: Option<space::services::dto::UserDto>,
}
impl From<repo_tag_protection_whitelists::Model> for TagWhitelistDto {
fn from(m: repo_tag_protection_whitelists::Model) -> Self {
Self {
policy_id: m.policy_id,
user_id: m.user_id,
user: None,
}
}
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct AddTagWhitelistCmd {
pub user_id: i64,
}
impl GitService {
async fn embed_whitelist_users(
&self,
mut rows: Vec<BranchWhitelistDto>,
) -> Result<Vec<BranchWhitelistDto>, GitError> {
let uids: Vec<i64> = rows.iter().filter_map(|r| r.user_id).collect();
if !uids.is_empty() {
let map = self.user_dto_map(&uids).await?;
for row in rows.iter_mut() {
row.user = row.user_id.and_then(|u| map.get(&u).cloned());
}
}
Ok(rows)
}
pub(crate) async fn user_dto_map(
&self,
uids: &[i64],
) -> Result<std::collections::HashMap<i64, space::services::dto::UserDto>, GitError> {
let mut map = std::collections::HashMap::new();
if uids.is_empty() {
return Ok(map);
}
let rows = export::users::load_public_users(&self.state, uids).await?;
for (uid, user) in rows {
map.insert(uid, space::services::dto::UserDto::from_public(&user));
}
Ok(map)
}
async fn ensure_branch_policy(
&self,
policy_id: Uuid,
) -> Result<repo_branch_protection_policy::Model, GitError> {
repo_branch_protection_policy::Entity::find_by_id(policy_id)
.one(self.db_read())
.await?
.ok_or(GitError::NotFound)
}
async fn ensure_tag_policy(
&self,
policy_id: Uuid,
) -> Result<repo_tag_protection_policy::Model, GitError> {
repo_tag_protection_policy::Entity::find_by_id(policy_id)
.one(self.db_read())
.await?
.ok_or(GitError::NotFound)
}
pub async fn add_branch_whitelist(
&self,
repo_id: Uuid,
policy_id: Uuid,
cmd: AddBranchWhitelistCmd,
) -> Result<BranchWhitelistDto, GitError> {
let policy = self.ensure_branch_policy(policy_id).await?;
if policy.repo_id != repo_id {
return Err(GitError::NotFound);
}
if cmd.user_id.is_some() == cmd.group_id.is_some() {
return Err(GitError::InvalidArgument(
"exactly one of user_id or group_id must be set".to_string(),
));
}
let existing = repo_branch_protection_whitelists::Entity::find()
.filter(repo_branch_protection_whitelists::Column::PolicyId.eq(policy_id))
.filter(repo_branch_protection_whitelists::Column::Kind.eq(cmd.kind))
.filter(repo_branch_protection_whitelists::Column::UserId.eq(cmd.user_id))
.filter(repo_branch_protection_whitelists::Column::GroupId.eq(cmd.group_id))
.one(self.db_read())
.await?;
if let Some(row) = existing {
return Ok(self
.embed_whitelist_users(vec![row.into()])
.await?
.into_iter()
.next()
.unwrap());
}
let am = repo_branch_protection_whitelists::ActiveModel {
id: Set(Uuid::now_v7()),
policy_id: Set(policy_id),
kind: Set(cmd.kind),
user_id: Set(cmd.user_id),
group_id: Set(cmd.group_id),
};
let saved = am.insert(self.db_write()).await?;
Ok(self
.embed_whitelist_users(vec![saved.into()])
.await?
.into_iter()
.next()
.unwrap())
}
pub async fn remove_branch_whitelist(
&self,
repo_id: Uuid,
policy_id: Uuid,
whitelist_id: Uuid,
) -> Result<(), GitError> {
let policy = self.ensure_branch_policy(policy_id).await?;
if policy.repo_id != repo_id {
return Err(GitError::NotFound);
}
repo_branch_protection_whitelists::Entity::delete_by_id(whitelist_id)
.filter(repo_branch_protection_whitelists::Column::PolicyId.eq(policy_id))
.exec(self.db_write())
.await?;
Ok(())
}
pub async fn list_branch_whitelists(
&self,
repo_id: Uuid,
policy_id: Uuid,
) -> Result<Vec<BranchWhitelistDto>, GitError> {
let policy = self.ensure_branch_policy(policy_id).await?;
if policy.repo_id != repo_id {
return Err(GitError::NotFound);
}
let rows = repo_branch_protection_whitelists::Entity::find()
.filter(repo_branch_protection_whitelists::Column::PolicyId.eq(policy_id))
.all(self.db_read())
.await?;
self.embed_whitelist_users(rows.into_iter().map(Into::into).collect())
.await
}
pub async fn add_tag_whitelist(
&self,
repo_id: Uuid,
policy_id: Uuid,
cmd: AddTagWhitelistCmd,
) -> Result<TagWhitelistDto, GitError> {
let policy = self.ensure_tag_policy(policy_id).await?;
if policy.repo_id != repo_id {
return Err(GitError::NotFound);
}
let existing = repo_tag_protection_whitelists::Entity::find_by_id((policy_id, cmd.user_id))
.one(self.db_read())
.await?;
if let Some(row) = existing {
let mut dto: TagWhitelistDto = row.into();
dto.user = self
.user_dto_map(&[dto.user_id])
.await?
.get(&dto.user_id)
.cloned();
return Ok(dto);
}
let am = repo_tag_protection_whitelists::ActiveModel {
policy_id: Set(policy_id),
user_id: Set(cmd.user_id),
};
let saved = TransactionTrait::transaction(self.db_write(), |txn| {
Box::pin(async move { am.insert(txn).await.map_err(GitError::from) })
})
.await?;
let mut dto: TagWhitelistDto = saved.into();
dto.user = self
.user_dto_map(&[dto.user_id])
.await?
.get(&dto.user_id)
.cloned();
Ok(dto)
}
pub async fn remove_tag_whitelist(
&self,
repo_id: Uuid,
policy_id: Uuid,
user_id: i64,
) -> Result<(), GitError> {
let policy = self.ensure_tag_policy(policy_id).await?;
if policy.repo_id != repo_id {
return Err(GitError::NotFound);
}
repo_tag_protection_whitelists::Entity::delete_by_id((policy_id, user_id))
.exec(self.db_write())
.await?;
Ok(())
}
pub async fn list_tag_whitelists(
&self,
repo_id: Uuid,
policy_id: Uuid,
) -> Result<Vec<TagWhitelistDto>, GitError> {
let policy = self.ensure_tag_policy(policy_id).await?;
if policy.repo_id != repo_id {
return Err(GitError::NotFound);
}
let rows = repo_tag_protection_whitelists::Entity::find()
.filter(repo_tag_protection_whitelists::Column::PolicyId.eq(policy_id))
.all(self.db_read())
.await?;
let mut out: Vec<TagWhitelistDto> = rows.into_iter().map(Into::into).collect();
let uids: Vec<i64> = out.iter().map(|t| t.user_id).collect();
let map = self.user_dto_map(&uids).await?;
for t in out.iter_mut() {
t.user = map.get(&t.user_id).cloned();
}
Ok(out)
}
}