use std::{collections::HashMap, sync::OnceLock};
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::entities::{MongoId, I64};
use super::{permission::PermissionLevel, ResourceTargetVariant};
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(
feature = "mongo",
derive(mongo_indexed::derive::MongoIndexed)
)]
#[cfg_attr(feature = "mongo", doc_index({ "config.type": 1 }))]
#[cfg_attr(feature = "mongo", sparse_doc_index({ "config.data.google_id": 1 }))]
#[cfg_attr(feature = "mongo", sparse_doc_index({ "config.data.github_id": 1 }))]
pub struct User {
#[serde(
default,
rename = "_id",
skip_serializing_if = "String::is_empty",
with = "bson::serde_helpers::hex_string_as_object_id"
)]
pub id: MongoId,
#[cfg_attr(feature = "mongo", unique_index)]
pub username: String,
#[cfg_attr(feature = "mongo", index)]
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub super_admin: bool,
#[serde(default)]
pub admin: bool,
#[serde(default)]
pub create_server_permissions: bool,
#[serde(default)]
pub create_build_permissions: bool,
pub config: UserConfig,
#[serde(default)]
pub last_update_view: I64,
#[serde(default)]
pub recents: HashMap<ResourceTargetVariant, Vec<String>>,
#[serde(default)]
pub all: HashMap<ResourceTargetVariant, PermissionLevel>,
#[serde(default)]
pub updated_at: I64,
}
impl User {
pub fn sanitize(&mut self) {
if let UserConfig::Local { .. } = &self.config {
self.config = UserConfig::default();
}
}
pub fn is_service_user(user_id: &str) -> bool {
matches!(
user_id,
"System"
| "000000000000000000000000"
| "Procedure"
| "000000000000000000000001"
| "Action"
| "000000000000000000000002"
| "Git Webhook"
| "000000000000000000000003"
| "Auto Redeploy"
| "000000000000000000000004"
| "Resource Sync"
| "000000000000000000000005"
| "Stack Wizard"
| "000000000000000000000006"
| "Build Manager"
| "000000000000000000000007"
| "Repo Manager"
| "000000000000000000000008"
)
}
}
pub fn admin_service_user(user_id: &str) -> Option<User> {
match user_id {
"000000000000000000000000" | "System" => {
system_user().to_owned().into()
}
"000000000000000000000001" | "Procedure" => {
procedure_user().to_owned().into()
}
"000000000000000000000002" | "Action" => {
action_user().to_owned().into()
}
"000000000000000000000003" | "Git Webhook" => {
git_webhook_user().to_owned().into()
}
"000000000000000000000004" | "Auto Redeploy" => {
auto_redeploy_user().to_owned().into()
}
"000000000000000000000005" | "Resource Sync" => {
sync_user().to_owned().into()
}
"000000000000000000000006" | "Stack Wizard" => {
stack_user().to_owned().into()
}
"000000000000000000000007" | "Build Manager" => {
build_user().to_owned().into()
}
"000000000000000000000008" | "Repo Manager" => {
repo_user().to_owned().into()
}
_ => None,
}
}
pub fn system_user() -> &'static User {
static SYSTEM_USER: OnceLock<User> = OnceLock::new();
SYSTEM_USER.get_or_init(|| {
let id_name = String::from("System");
User {
id: "000000000000000000000000".to_string(),
username: id_name,
enabled: true,
admin: true,
..Default::default()
}
})
}
pub fn procedure_user() -> &'static User {
static PROCEDURE_USER: OnceLock<User> = OnceLock::new();
PROCEDURE_USER.get_or_init(|| {
let id_name = String::from("Procedure");
User {
id: "000000000000000000000001".to_string(),
username: id_name,
enabled: true,
admin: true,
..Default::default()
}
})
}
pub fn action_user() -> &'static User {
static ACTION_USER: OnceLock<User> = OnceLock::new();
ACTION_USER.get_or_init(|| {
let id_name = String::from("Action");
User {
id: "000000000000000000000002".to_string(),
username: id_name,
enabled: true,
admin: true,
..Default::default()
}
})
}
pub fn git_webhook_user() -> &'static User {
static GIT_WEBHOOK_USER: OnceLock<User> = OnceLock::new();
GIT_WEBHOOK_USER.get_or_init(|| {
let id_name = String::from("Git Webhook");
User {
id: "000000000000000000000003".to_string(),
username: id_name,
enabled: true,
admin: true,
..Default::default()
}
})
}
pub fn auto_redeploy_user() -> &'static User {
static AUTO_REDEPLOY_USER: OnceLock<User> = OnceLock::new();
AUTO_REDEPLOY_USER.get_or_init(|| {
let id_name = String::from("Auto Redeploy");
User {
id: "000000000000000000000004".to_string(),
username: id_name,
enabled: true,
admin: true,
..Default::default()
}
})
}
pub fn sync_user() -> &'static User {
static SYNC_USER: OnceLock<User> = OnceLock::new();
SYNC_USER.get_or_init(|| {
let id_name = String::from("Resource Sync");
User {
id: "000000000000000000000005".to_string(),
username: id_name,
enabled: true,
admin: true,
..Default::default()
}
})
}
pub fn stack_user() -> &'static User {
static STACK_USER: OnceLock<User> = OnceLock::new();
STACK_USER.get_or_init(|| {
let id_name = String::from("Stack Wizard");
User {
id: "000000000000000000000006".to_string(),
username: id_name,
enabled: true,
admin: true,
..Default::default()
}
})
}
pub fn build_user() -> &'static User {
static BUILD_USER: OnceLock<User> = OnceLock::new();
BUILD_USER.get_or_init(|| {
let id_name = String::from("Build Manager");
User {
id: "000000000000000000000007".to_string(),
username: id_name,
enabled: true,
admin: true,
..Default::default()
}
})
}
pub fn repo_user() -> &'static User {
static REPO_USER: OnceLock<User> = OnceLock::new();
REPO_USER.get_or_init(|| {
let id_name = String::from("Repo Manager");
User {
id: "000000000000000000000008".to_string(),
username: id_name,
enabled: true,
admin: true,
..Default::default()
}
})
}
#[typeshare]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum UserConfig {
Local { password: String },
Google { google_id: String, avatar: String },
Github { github_id: String, avatar: String },
Oidc { provider: String, user_id: String },
Service { description: String },
}
impl Default for UserConfig {
fn default() -> Self {
Self::Local {
password: String::new(),
}
}
}