use std::time::{Duration, SystemTime};
#[derive(Debug, Clone)]
pub struct ApiKey {
key: String,
name: Option<String>,
owner: Option<String>,
roles: Vec<String>,
authorities: Vec<String>,
enabled: bool,
created_at: SystemTime,
expires_at: Option<SystemTime>,
metadata: std::collections::HashMap<String, String>,
}
impl ApiKey {
pub fn new(key: impl Into<String>) -> Self {
Self {
key: key.into(),
name: None,
owner: None,
roles: Vec::new(),
authorities: Vec::new(),
enabled: true,
created_at: SystemTime::now(),
expires_at: None,
metadata: std::collections::HashMap::new(),
}
}
pub fn builder(key: impl Into<String>) -> ApiKeyBuilder {
ApiKeyBuilder::new(key)
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn owner(mut self, owner: impl Into<String>) -> Self {
self.owner = Some(owner.into());
self
}
pub fn roles(mut self, roles: Vec<String>) -> Self {
self.roles = roles;
self
}
pub fn authorities(mut self, authorities: Vec<String>) -> Self {
self.authorities = authorities;
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn expires_at(mut self, expires_at: SystemTime) -> Self {
self.expires_at = Some(expires_at);
self
}
pub fn expires_in(mut self, duration: Duration) -> Self {
self.expires_at = Some(SystemTime::now() + duration);
self
}
pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
pub fn get_key(&self) -> &str {
&self.key
}
pub fn get_name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn get_owner(&self) -> Option<&str> {
self.owner.as_deref()
}
pub fn get_roles(&self) -> &[String] {
&self.roles
}
pub fn get_authorities(&self) -> &[String] {
&self.authorities
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn get_created_at(&self) -> SystemTime {
self.created_at
}
pub fn get_expires_at(&self) -> Option<SystemTime> {
self.expires_at
}
pub fn get_metadata(&self) -> &std::collections::HashMap<String, String> {
&self.metadata
}
pub fn is_expired(&self) -> bool {
if let Some(expires_at) = self.expires_at {
SystemTime::now() > expires_at
} else {
false
}
}
pub fn is_valid(&self) -> bool {
self.enabled && !self.is_expired()
}
pub fn has_role(&self, role: &str) -> bool {
self.roles.iter().any(|r| r == role)
}
pub fn has_any_role(&self, roles: &[&str]) -> bool {
roles.iter().any(|role| self.has_role(role))
}
pub fn has_authority(&self, authority: &str) -> bool {
self.authorities.iter().any(|a| a == authority)
}
pub fn has_any_authority(&self, authorities: &[&str]) -> bool {
authorities.iter().any(|auth| self.has_authority(auth))
}
}
#[derive(Debug, Clone)]
pub struct ApiKeyBuilder {
key: ApiKey,
}
impl ApiKeyBuilder {
pub fn new(key: impl Into<String>) -> Self {
Self {
key: ApiKey::new(key),
}
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.key.name = Some(name.into());
self
}
pub fn owner(mut self, owner: impl Into<String>) -> Self {
self.key.owner = Some(owner.into());
self
}
pub fn roles(mut self, roles: Vec<String>) -> Self {
self.key.roles = roles;
self
}
pub fn role(mut self, role: impl Into<String>) -> Self {
self.key.roles.push(role.into());
self
}
pub fn authorities(mut self, authorities: Vec<String>) -> Self {
self.key.authorities = authorities;
self
}
pub fn authority(mut self, authority: impl Into<String>) -> Self {
self.key.authorities.push(authority.into());
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.key.enabled = enabled;
self
}
pub fn expires_at(mut self, expires_at: SystemTime) -> Self {
self.key.expires_at = Some(expires_at);
self
}
pub fn expires_in(mut self, duration: Duration) -> Self {
self.key.expires_at = Some(SystemTime::now() + duration);
self
}
pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.key.metadata.insert(key.into(), value.into());
self
}
pub fn build(self) -> ApiKey {
self.key
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_api_key_creation() {
let key = ApiKey::new("sk_test_123");
assert_eq!(key.get_key(), "sk_test_123");
assert!(key.is_enabled());
assert!(!key.is_expired());
assert!(key.is_valid());
}
#[test]
fn test_api_key_with_roles() {
let key = ApiKey::new("sk_test_123").roles(vec!["USER".into(), "ADMIN".into()]);
assert!(key.has_role("USER"));
assert!(key.has_role("ADMIN"));
assert!(!key.has_role("SUPER_ADMIN"));
assert!(key.has_any_role(&["USER", "GUEST"]));
}
#[test]
fn test_api_key_with_authorities() {
let key =
ApiKey::new("sk_test_123").authorities(vec!["api:read".into(), "api:write".into()]);
assert!(key.has_authority("api:read"));
assert!(key.has_authority("api:write"));
assert!(!key.has_authority("api:admin"));
assert!(key.has_any_authority(&["api:read", "api:delete"]));
}
#[test]
fn test_api_key_disabled() {
let key = ApiKey::new("sk_test_123").enabled(false);
assert!(!key.is_enabled());
assert!(!key.is_valid());
}
#[test]
fn test_api_key_expired() {
let key = ApiKey::new("sk_test_123").expires_at(SystemTime::UNIX_EPOCH);
assert!(key.is_expired());
assert!(!key.is_valid());
}
#[test]
fn test_api_key_builder() {
let key = ApiKey::builder("sk_test_123")
.name("Test Key")
.owner("test@example.com")
.role("USER")
.role("ADMIN")
.authority("api:read")
.metadata("environment", "test")
.build();
assert_eq!(key.get_name(), Some("Test Key"));
assert_eq!(key.get_owner(), Some("test@example.com"));
assert!(key.has_role("USER"));
assert!(key.has_role("ADMIN"));
assert!(key.has_authority("api:read"));
assert_eq!(
key.get_metadata().get("environment"),
Some(&"test".to_string())
);
}
}