use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
use uuid::Uuid;
pub const DEFAULT_ORG_ID: i64 = 1;
pub const DEFAULT_ORG_PUBLIC_ID: &str = "org_00000000000000000000000000000001";
pub const ANONYMOUS_USER_ID: Uuid = Uuid::from_bytes([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
]);
pub const ANONYMOUS_USER_EMAIL: &str = "anonymous@local";
pub const ANONYMOUS_USER_NAME: &str = "Anonymous";
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct Organization {
pub public_id: String,
pub name: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "lowercase")]
pub enum OrgRole {
Member,
Admin,
#[default]
Owner,
}
impl OrgRole {
pub fn has_permission(self, required: OrgRole) -> bool {
self.level() >= required.level()
}
pub fn as_str(self) -> &'static str {
match self {
OrgRole::Member => "member",
OrgRole::Admin => "admin",
OrgRole::Owner => "owner",
}
}
fn level(self) -> u8 {
match self {
OrgRole::Member => 0,
OrgRole::Admin => 1,
OrgRole::Owner => 2,
}
}
}
impl fmt::Display for OrgRole {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for OrgRole {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"member" => Ok(OrgRole::Member),
"admin" => Ok(OrgRole::Admin),
"owner" => Ok(OrgRole::Owner),
_ => Err(format!("invalid org role: {s}")),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct OrgMembership {
#[serde(skip_serializing)]
pub org_id: i64,
pub public_id: String,
pub name: String,
#[serde(default)]
pub role: OrgRole,
}
pub fn org_public_id_from_internal(org_id: i64) -> String {
if org_id == DEFAULT_ORG_ID {
return DEFAULT_ORG_PUBLIC_ID.to_string();
}
format!("org_{:032x}", org_id)
}
pub fn generate_org_public_id() -> String {
let uuid = Uuid::new_v4();
format!("org_{}", uuid.simple())
}
pub fn validate_org_public_id(public_id: &str) -> bool {
if !public_id.starts_with("org_") {
return false;
}
let suffix = &public_id[4..];
suffix.len() == 32
&& suffix
.chars()
.all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_org_public_id() {
let id = generate_org_public_id();
assert!(id.starts_with("org_"));
assert_eq!(id.len(), 36); assert!(validate_org_public_id(&id));
}
#[test]
fn test_validate_org_public_id() {
assert!(validate_org_public_id(
"org_00000000000000000000000000000001"
));
assert!(validate_org_public_id(
"org_2f3c1b3e6a9d4c6f8a1d4e9c9b7f21a0"
));
assert!(!validate_org_public_id(
"organization_12345678901234567890123456789012"
));
assert!(!validate_org_public_id("org_123"));
assert!(!validate_org_public_id(
"org_123456789012345678901234567890123"
));
assert!(!validate_org_public_id(
"org_2F3C1B3E6A9D4C6F8A1D4E9C9B7F21A0"
));
assert!(!validate_org_public_id(
"org_ghijklmnopqrstuvwxyz1234567890"
));
}
#[test]
fn test_default_org_public_id_valid() {
assert!(validate_org_public_id(DEFAULT_ORG_PUBLIC_ID));
}
#[test]
fn test_org_role_hierarchy() {
assert!(OrgRole::Owner.has_permission(OrgRole::Owner));
assert!(OrgRole::Owner.has_permission(OrgRole::Admin));
assert!(OrgRole::Owner.has_permission(OrgRole::Member));
assert!(!OrgRole::Admin.has_permission(OrgRole::Owner));
assert!(OrgRole::Admin.has_permission(OrgRole::Admin));
assert!(OrgRole::Admin.has_permission(OrgRole::Member));
assert!(!OrgRole::Member.has_permission(OrgRole::Owner));
assert!(!OrgRole::Member.has_permission(OrgRole::Admin));
assert!(OrgRole::Member.has_permission(OrgRole::Member));
}
#[test]
fn test_org_role_str_roundtrip() {
for role in [OrgRole::Member, OrgRole::Admin, OrgRole::Owner] {
let s = role.as_str();
let parsed: OrgRole = s.parse().unwrap();
assert_eq!(parsed, role);
}
}
#[test]
fn test_org_role_default() {
assert_eq!(OrgRole::default(), OrgRole::Owner);
}
}