use anyhow::{Context, Result};
use sqlx::PgPool;
use uuid::Uuid;
use crate::db::{models::TeamRole, teams};
pub async fn sync_user_groups(pool: &PgPool, user_id: Uuid, idp_groups: &[String]) -> Result<()> {
let mut tx = pool.begin().await.context("Failed to start transaction")?;
tracing::debug!(
"Syncing {} IdP groups for user {}",
idp_groups.len(),
user_id
);
for group_name in idp_groups {
let existing_team = teams::find_by_name(&mut *tx, group_name)
.await
.context("Failed to find team by name")?;
let team_id = if let Some(team) = existing_team {
if team.name != *group_name {
tracing::info!(
"Updating team name from '{}' to '{}' (IdP case correction)",
team.name,
group_name
);
teams::update_name(&mut *tx, team.id, group_name)
.await
.context("Failed to update team name")?;
}
if !team.idp_managed {
tracing::info!(
"Converting team '{}' to IdP-managed (removing all owners)",
group_name
);
teams::set_idp_managed(&mut *tx, team.id, true)
.await
.context("Failed to mark team as IdP-managed")?;
teams::remove_all_owners(&mut *tx, team.id)
.await
.context("Failed to remove owners")?;
}
team.id
} else {
tracing::info!("Creating new IdP-managed team '{}'", group_name);
let new_team = teams::create(&mut *tx, group_name)
.await
.context("Failed to create team")?;
teams::set_idp_managed(&mut *tx, new_team.id, true)
.await
.context("Failed to mark new team as IdP-managed")?;
new_team.id
};
let is_member = teams::is_member(&mut *tx, team_id, user_id)
.await
.context("Failed to check membership")?;
if !is_member {
tracing::debug!("Adding user to IdP-managed team '{}'", group_name);
teams::add_member(&mut *tx, team_id, user_id, TeamRole::Member)
.await
.context("Failed to add user to team")?;
}
}
let all_idp_teams = teams::list_idp_managed(&mut *tx)
.await
.context("Failed to list IdP-managed teams")?;
for team in all_idp_teams {
let in_claim = idp_groups
.iter()
.any(|g| g.eq_ignore_ascii_case(&team.name));
if !in_claim {
let is_member = teams::is_member(&mut *tx, team.id, user_id)
.await
.context("Failed to check membership")?;
if is_member {
tracing::info!(
"Removing user from IdP-managed team '{}' (not in groups claim)",
team.name
);
teams::remove_all_user_roles(&mut *tx, team.id, user_id)
.await
.context("Failed to remove user from team")?;
}
}
}
tx.commit().await.context("Failed to commit transaction")?;
tracing::debug!("Successfully synced IdP groups for user {}", user_id);
Ok(())
}