use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct LoginRequest {
pub username: String,
pub password: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct RefreshTokenRequest {
pub refresh_token: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct AuthResponse {
pub token: String,
pub token_type: String,
pub expires_in: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct AuthRequest {
pub username: String,
pub password: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, PartialEq, Eq, Hash)]
pub enum Permission {
#[serde(rename = "embedding:read")]
EmbedRead,
#[serde(rename = "embedding:write")]
EmbedWrite,
#[serde(rename = "model:read")]
ModelRead,
#[serde(rename = "model:write")]
ModelWrite,
#[serde(rename = "model:switch")]
ModelSwitch,
#[serde(rename = "user:read")]
UserRead,
#[serde(rename = "user:write")]
UserWrite,
#[serde(rename = "user:delete")]
UserDelete,
#[serde(rename = "admin")]
Admin,
}
impl Permission {
pub fn as_str(&self) -> &'static str {
match self {
Permission::EmbedRead => "embedding:read",
Permission::EmbedWrite => "embedding:write",
Permission::ModelRead => "model:read",
Permission::ModelWrite => "model:write",
Permission::ModelSwitch => "model:switch",
Permission::UserRead => "user:read",
Permission::UserWrite => "user:write",
Permission::UserDelete => "user:delete",
Permission::Admin => "admin",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Option<Self> {
match s {
"embedding:read" => Some(Permission::EmbedRead),
"embedding:write" => Some(Permission::EmbedWrite),
"model:read" => Some(Permission::ModelRead),
"model:write" => Some(Permission::ModelWrite),
"model:switch" => Some(Permission::ModelSwitch),
"user:read" => Some(Permission::UserRead),
"user:write" => Some(Permission::UserWrite),
"user:delete" => Some(Permission::UserDelete),
"admin" => Some(Permission::Admin),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub username: String,
pub role: String,
pub permissions: Vec<String>,
}
impl User {
pub fn has_permission(&self, permission: &str) -> bool {
self.permissions.contains(&permission.to_string()) || self.role == "admin"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_permission_as_str() {
assert_eq!(Permission::EmbedRead.as_str(), "embedding:read");
assert_eq!(Permission::EmbedWrite.as_str(), "embedding:write");
assert_eq!(Permission::ModelRead.as_str(), "model:read");
assert_eq!(Permission::ModelWrite.as_str(), "model:write");
assert_eq!(Permission::ModelSwitch.as_str(), "model:switch");
assert_eq!(Permission::UserRead.as_str(), "user:read");
assert_eq!(Permission::UserWrite.as_str(), "user:write");
assert_eq!(Permission::UserDelete.as_str(), "user:delete");
assert_eq!(Permission::Admin.as_str(), "admin");
}
#[test]
fn test_permission_from_str_valid() {
assert_eq!(
Permission::from_str("embedding:read"),
Some(Permission::EmbedRead)
);
assert_eq!(
Permission::from_str("embedding:write"),
Some(Permission::EmbedWrite)
);
assert_eq!(
Permission::from_str("model:read"),
Some(Permission::ModelRead)
);
assert_eq!(
Permission::from_str("model:write"),
Some(Permission::ModelWrite)
);
assert_eq!(
Permission::from_str("model:switch"),
Some(Permission::ModelSwitch)
);
assert_eq!(
Permission::from_str("user:read"),
Some(Permission::UserRead)
);
assert_eq!(
Permission::from_str("user:write"),
Some(Permission::UserWrite)
);
assert_eq!(
Permission::from_str("user:delete"),
Some(Permission::UserDelete)
);
assert_eq!(Permission::from_str("admin"), Some(Permission::Admin));
}
#[test]
fn test_permission_from_str_invalid() {
assert_eq!(Permission::from_str("invalid"), None);
assert_eq!(Permission::from_str(""), None);
assert_eq!(Permission::from_str("embedding"), None);
}
#[test]
fn test_permission_roundtrip_serialization() {
let perm = Permission::EmbedRead;
let json = serde_json::to_string(&perm).unwrap();
assert_eq!(json, r#""embedding:read""#);
let deserialized: Permission = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, perm);
}
#[test]
fn test_permission_equality() {
assert_eq!(Permission::Admin, Permission::Admin);
assert_ne!(Permission::Admin, Permission::EmbedRead);
}
#[test]
fn test_login_request_serialization() {
let req = LoginRequest {
username: "alice".to_string(),
password: "secret".to_string(),
};
let json = serde_json::to_string(&req).unwrap();
let deserialized: LoginRequest = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.username, "alice");
assert_eq!(deserialized.password, "secret");
}
#[test]
fn test_auth_response_serialization() {
let resp = AuthResponse {
token: "tok123".to_string(),
token_type: "Bearer".to_string(),
expires_in: 3600,
};
let json = serde_json::to_string(&resp).unwrap();
let deserialized: AuthResponse = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.token, "tok123");
assert_eq!(deserialized.token_type, "Bearer");
assert_eq!(deserialized.expires_in, 3600);
}
#[test]
fn test_user_has_permission_with_explicit_permission() {
let user = User {
username: "alice".to_string(),
role: "user".to_string(),
permissions: vec!["embedding:read".to_string()],
};
assert!(user.has_permission("embedding:read"));
assert!(!user.has_permission("embedding:write"));
}
#[test]
fn test_user_has_permission_admin_role() {
let user = User {
username: "bob".to_string(),
role: "admin".to_string(),
permissions: vec![],
};
assert!(user.has_permission("embedding:read"));
assert!(user.has_permission("any_permission"));
}
#[test]
fn test_user_has_permission_no_match() {
let user = User {
username: "charlie".to_string(),
role: "user".to_string(),
permissions: vec!["model:read".to_string()],
};
assert!(!user.has_permission("embedding:read"));
}
#[test]
fn test_refresh_token_request_serialization() {
let req = RefreshTokenRequest {
refresh_token: "refresh_tok".to_string(),
};
let json = serde_json::to_string(&req).unwrap();
let deserialized: RefreshTokenRequest = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.refresh_token, "refresh_tok");
}
#[test]
fn test_auth_request_serialization() {
let req = AuthRequest {
username: "user1".to_string(),
password: "pass1".to_string(),
};
let json = serde_json::to_string(&req).unwrap();
let deserialized: AuthRequest = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.username, "user1");
assert_eq!(deserialized.password, "pass1");
}
}