use crate::audit::SecurityAuditStats;
use crate::auth::{AuthFramework, AuthStats, UserInfo};
use crate::errors::{AuthError, Result};
use crate::maintenance::{BackupReport, ResetReport, RestoreReport};
use crate::methods::MfaChallenge;
use crate::permissions::Role;
use crate::storage::SessionData;
use crate::tokens::AuthToken;
use std::sync::Arc;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ExecutionMode {
DryRun,
Execute,
}
impl ExecutionMode {
pub fn is_dry_run(self) -> bool {
matches!(self, Self::DryRun)
}
}
impl From<ExecutionMode> for bool {
fn from(mode: ExecutionMode) -> bool {
mode.is_dry_run()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UserStatus {
Active,
Inactive,
}
impl UserStatus {
pub fn is_active(self) -> bool {
matches!(self, Self::Active)
}
}
impl From<UserStatus> for bool {
fn from(status: UserStatus) -> bool {
status.is_active()
}
}
impl From<bool> for UserStatus {
fn from(active: bool) -> Self {
if active { Self::Active } else { Self::Inactive }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SessionFilter {
ActiveOnly,
IncludeInactive,
}
impl SessionFilter {
pub fn include_inactive(self) -> bool {
matches!(self, Self::IncludeInactive)
}
}
impl From<SessionFilter> for bool {
fn from(filter: SessionFilter) -> bool {
filter.include_inactive()
}
}
#[derive(Debug, Clone, Default)]
pub struct AuditLogQuery {
user_id: Option<String>,
action: Option<String>,
resource: Option<String>,
limit: Option<usize>,
}
impl AuditLogQuery {
pub fn new() -> Self {
Self::default()
}
pub fn user(mut self, user_id: impl Into<String>) -> Self {
self.user_id = Some(user_id.into());
self
}
pub fn action(mut self, action: impl Into<String>) -> Self {
self.action = Some(action.into());
self
}
pub fn resource(mut self, resource: impl Into<String>) -> Self {
self.resource = Some(resource.into());
self
}
pub fn limit(mut self, limit: usize) -> Self {
self.limit = Some(limit);
self
}
pub fn get_user_id(&self) -> Option<&str> {
self.user_id.as_deref()
}
pub fn get_action(&self) -> Option<&str> {
self.action.as_deref()
}
pub fn get_resource(&self) -> Option<&str> {
self.resource.as_deref()
}
pub fn get_limit(&self) -> Option<usize> {
self.limit
}
}
#[derive(Debug, Clone, Default)]
pub struct UserListQuery {
limit: Option<usize>,
offset: Option<usize>,
active_only: bool,
}
impl UserListQuery {
pub fn new() -> Self {
Self::default()
}
pub fn limit(mut self, limit: usize) -> Self {
self.limit = Some(limit);
self
}
pub fn offset(mut self, offset: usize) -> Self {
self.offset = Some(offset);
self
}
pub fn active_only(mut self) -> Self {
self.active_only = true;
self
}
pub fn active_only_if(mut self, active_only: bool) -> Self {
self.active_only = active_only;
self
}
pub fn limit_if_some(mut self, limit: Option<usize>) -> Self {
if let Some(limit) = limit {
self.limit = Some(limit);
}
self
}
pub fn get_limit(&self) -> Option<usize> {
self.limit
}
pub fn get_offset(&self) -> Option<usize> {
self.offset
}
pub fn get_active_only(&self) -> bool {
self.active_only
}
}
#[derive(Debug, Clone, Default)]
pub struct PermissionContext {
attributes: std::collections::HashMap<String, String>,
}
impl PermissionContext {
pub fn new() -> Self {
Self::default()
}
pub fn with_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.attributes.insert(key.into(), value.into());
self
}
pub fn with_attributes<I, K, V>(mut self, attributes: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: Into<String>,
V: Into<String>,
{
for (key, value) in attributes {
self.attributes.insert(key.into(), value.into());
}
self
}
pub fn into_attributes(self) -> std::collections::HashMap<String, String> {
self.attributes
}
pub fn attributes(&self) -> &std::collections::HashMap<String, String> {
&self.attributes
}
}
#[derive(Debug, Clone)]
pub struct DelegationRequest {
delegator_id: String,
delegatee_id: String,
action: String,
resource: String,
duration: Duration,
}
impl DelegationRequest {
pub fn new(
delegator_id: impl Into<String>,
delegatee_id: impl Into<String>,
action: impl Into<String>,
resource: impl Into<String>,
) -> Self {
Self {
delegator_id: delegator_id.into(),
delegatee_id: delegatee_id.into(),
action: action.into(),
resource: resource.into(),
duration: Duration::from_secs(3600),
}
}
pub fn duration(mut self, duration: Duration) -> Self {
self.duration = duration;
self
}
pub fn delegator_id(&self) -> &str {
&self.delegator_id
}
pub fn delegatee_id(&self) -> &str {
&self.delegatee_id
}
pub fn action(&self) -> &str {
&self.action
}
pub fn resource(&self) -> &str {
&self.resource
}
pub fn get_duration(&self) -> Duration {
self.duration
}
}
pub struct UserOperations<'a> {
pub(crate) framework: &'a AuthFramework,
}
impl UserOperations<'_> {
pub async fn register(&self, username: &str, email: &str, password: &str) -> Result<String> {
self.framework
.register_user(username, email, password)
.await
}
#[deprecated(
since = "0.6.0",
note = "use `list_with_query(UserListQuery::new().limit(n).active_only())` instead"
)]
pub async fn list(
&self,
limit: Option<usize>,
offset: Option<usize>,
active_only: bool,
) -> Result<Vec<UserInfo>> {
self.framework
.list_users_with_query(
UserListQuery::new()
.offset(offset.unwrap_or(0))
.active_only_if(active_only)
.limit_if_some(limit),
)
.await
}
pub async fn list_with_query(&self, query: UserListQuery) -> Result<Vec<UserInfo>> {
self.framework
.list_users_with_query(query)
.await
}
pub async fn get(&self, user_id: &str) -> Result<UserInfo> {
self.framework.get_user_record(user_id).await
}
pub async fn exists_by_username(&self, username: &str) -> Result<bool> {
self.framework.username_exists(username).await
}
pub async fn exists_by_email(&self, email: &str) -> Result<bool> {
self.framework.email_exists(email).await
}
pub async fn get_by_username(
&self,
username: &str,
) -> Result<std::collections::HashMap<String, serde_json::Value>> {
self.framework.get_user_by_username(username).await
}
pub async fn profile(&self, user_id: &str) -> Result<crate::providers::ProviderProfile> {
self.framework.get_user_profile(user_id).await
}
pub async fn update_password(&self, username: &str, new_password: &str) -> Result<()> {
self.framework
.update_user_password(username, new_password)
.await
}
pub async fn update_password_by_id(&self, user_id: &str, new_password: &str) -> Result<()> {
self.framework
.update_user_password_by_id(user_id, new_password)
.await
}
pub async fn update_roles(&self, user_id: &str, roles: &[String]) -> Result<()> {
self.framework.update_user_roles(user_id, roles).await
}
#[deprecated(since = "0.5.0", note = "use `set_status(id, UserStatus)` instead")]
pub async fn set_active(&self, user_id: &str, active: bool) -> Result<()> {
self.framework.set_user_active(user_id, active).await
}
pub async fn set_status(&self, user_id: &str, status: UserStatus) -> Result<()> {
self.framework
.set_user_active(user_id, status.is_active())
.await
}
pub async fn update_email(&self, user_id: &str, email: &str) -> Result<()> {
self.framework.update_user_email(user_id, email).await
}
pub async fn verify_password(&self, user_id: &str, password: &str) -> Result<bool> {
self.framework.verify_user_password(user_id, password).await
}
pub async fn username(&self, user_id: &str) -> Result<String> {
self.framework.get_username_by_id(user_id).await
}
pub async fn delete(&self, username: &str) -> Result<()> {
self.framework.delete_user(username).await
}
pub async fn delete_by_id(&self, user_id: &str) -> Result<()> {
self.framework.delete_user_by_id(user_id).await
}
pub fn check_username(&self, username: &str) -> Result<()> {
crate::utils::validation::validate_username(username)
}
pub fn check_password_strength(&self, password: &str) -> Result<()> {
let strength = crate::utils::password::check_password_strength(password);
if crate::utils::password::meets_production_strength(strength.level) {
Ok(())
} else {
Err(AuthError::validation(format!(
"Password does not meet strength requirements: {}",
strength.feedback.join(", ")
)))
}
}
pub fn check_email(&self, email: &str) -> Result<()> {
crate::utils::validation::validate_email(email)
}
}
#[derive(Debug, Clone)]
pub struct SessionCreateRequest {
user_id: String,
expires_in: Duration,
ip_address: Option<String>,
user_agent: Option<String>,
}
impl SessionCreateRequest {
pub fn new(user_id: impl Into<String>, expires_in: Duration) -> Self {
Self {
user_id: user_id.into(),
expires_in,
ip_address: None,
user_agent: None,
}
}
pub fn ip_address(mut self, ip: impl Into<String>) -> Self {
self.ip_address = Some(ip.into());
self
}
pub fn user_agent(mut self, ua: impl Into<String>) -> Self {
self.user_agent = Some(ua.into());
self
}
pub fn get_user_id(&self) -> &str {
&self.user_id
}
pub fn get_expires_in(&self) -> Duration {
self.expires_in
}
pub fn get_ip_address(&self) -> Option<&str> {
self.ip_address.as_deref()
}
pub fn get_user_agent(&self) -> Option<&str> {
self.user_agent.as_deref()
}
}
pub struct SessionOperations<'a> {
pub(crate) framework: &'a AuthFramework,
}
impl SessionOperations<'_> {
pub async fn create_session(&self, req: SessionCreateRequest) -> Result<String> {
self.framework
.create_session(&req.user_id, req.expires_in, req.ip_address, req.user_agent)
.await
}
pub async fn create(
&self,
user_id: &str,
expires_in: Duration,
ip_address: Option<String>,
user_agent: Option<String>,
) -> Result<String> {
self.framework
.create_session(user_id, expires_in, ip_address, user_agent)
.await
}
pub async fn get(&self, session_id: &str) -> Result<Option<SessionData>> {
self.framework.get_session(session_id).await
}
pub async fn delete(&self, session_id: &str) -> Result<()> {
self.framework.delete_session(session_id).await
}
pub async fn list_for_user(&self, user_id: &str) -> Result<Vec<SessionData>> {
self.framework.storage().list_user_sessions(user_id).await
}
pub async fn list_for_user_filtered(
&self,
user_id: &str,
filter: SessionFilter,
) -> Result<Vec<SessionData>> {
let sessions = self.framework.storage().list_user_sessions(user_id).await?;
if filter.include_inactive() {
Ok(sessions)
} else {
Ok(sessions.into_iter().filter(|s| !s.is_expired()).collect())
}
}
pub async fn cleanup_expired(&self) -> Result<()> {
self.framework.cleanup_expired_data().await
}
}
#[derive(Debug, Clone)]
pub struct TokenCreateRequest {
user_id: String,
method: String,
scopes: Vec<String>,
lifetime: Option<Duration>,
}
impl TokenCreateRequest {
pub fn new(user_id: impl Into<String>, method: impl Into<String>) -> Self {
Self {
user_id: user_id.into(),
method: method.into(),
scopes: Vec::new(),
lifetime: None,
}
}
pub fn scope(mut self, scope: impl Into<String>) -> Self {
self.scopes.push(scope.into());
self
}
pub fn scopes<I, S>(mut self, scopes: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.scopes.extend(scopes.into_iter().map(Into::into));
self
}
pub fn lifetime(mut self, duration: Duration) -> Self {
self.lifetime = Some(duration);
self
}
}
pub struct TokenOperations<'a> {
pub(crate) framework: &'a AuthFramework,
}
impl TokenOperations<'_> {
pub async fn create<I, S>(
&self,
user_id: impl Into<String>,
scopes: I,
method_name: impl Into<String>,
lifetime: Option<Duration>,
) -> Result<AuthToken>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let scopes: Vec<String> = scopes.into_iter().map(|s| s.as_ref().to_owned()).collect();
self.framework
.create_auth_token(user_id, scopes, method_name, lifetime)
.await
}
pub async fn create_token(&self, req: TokenCreateRequest) -> Result<AuthToken> {
self.framework
.create_auth_token(req.user_id, req.scopes, req.method, req.lifetime)
.await
}
pub async fn validate(&self, token: &AuthToken) -> Result<bool> {
self.framework.validate_token(token).await
}
pub async fn refresh(&self, token: &AuthToken) -> Result<AuthToken> {
self.framework.refresh_token(token).await
}
pub async fn revoke(&self, token: &AuthToken) -> Result<()> {
self.framework.revoke_token(token).await
}
pub async fn list_for_user(&self, user_id: &str) -> Result<Vec<AuthToken>> {
self.framework.list_user_tokens(user_id).await
}
pub async fn create_api_key(
&self,
user_id: &str,
expires_in: Option<Duration>,
) -> Result<String> {
self.framework.create_api_key(user_id, expires_in).await
}
pub async fn validate_api_key(&self, api_key: &str) -> Result<UserInfo> {
self.framework.validate_api_key(api_key).await
}
pub async fn revoke_api_key(&self, api_key: &str) -> Result<()> {
self.framework.revoke_api_key(api_key).await
}
}
pub struct AuthorizationOperations<'a> {
pub(crate) framework: &'a AuthFramework,
}
impl AuthorizationOperations<'_> {
pub async fn check(&self, token: &AuthToken, action: &str, resource: &str) -> Result<bool> {
self.framework
.check_permission(token, action, resource)
.await
}
pub async fn grant(&self, user_id: &str, action: &str, resource: &str) -> Result<()> {
self.framework
.grant_permission(user_id, action, resource)
.await
}
pub async fn revoke(&self, user_id: &str, action: &str, resource: &str) -> Result<()> {
self.framework
.revoke_permission(user_id, action, resource)
.await
}
pub async fn create_role(&self, role: Role) -> Result<()> {
self.framework.create_role(role).await
}
pub async fn list_roles(&self) -> Vec<Role> {
self.framework.list_roles().await
}
pub async fn role(&self, role_name: &str) -> Result<Role> {
self.framework.get_role(role_name).await
}
pub async fn add_role_permission(
&self,
role_name: &str,
permission: crate::permissions::Permission,
) -> Result<()> {
self.framework
.add_role_permission(role_name, permission)
.await
}
pub async fn assign_role(&self, user_id: &str, role_name: &str) -> Result<()> {
self.framework.assign_role(user_id, role_name).await
}
pub async fn remove_role(&self, user_id: &str, role_name: &str) -> Result<()> {
self.framework.remove_role(user_id, role_name).await
}
pub async fn has_role(&self, user_id: &str, role_name: &str) -> Result<bool> {
self.framework.user_has_role(user_id, role_name).await
}
pub async fn effective_permissions(&self, user_id: &str) -> Result<crate::types::Permissions> {
self.framework
.get_effective_permissions(user_id)
.await
.map(crate::types::Permissions)
}
pub async fn roles_for_user(&self, user_id: &str) -> Result<crate::types::Roles> {
self.framework
.list_user_roles(user_id)
.await
.map(crate::types::Roles)
}
}
pub struct MaintenanceOperations<'a> {
pub(crate) framework: &'a AuthFramework,
}
impl MaintenanceOperations<'_> {
pub async fn backup(&self, output_path: &str, mode: ExecutionMode) -> Result<BackupReport> {
crate::maintenance::backup_to_file(self.framework, output_path, mode.is_dry_run()).await
}
pub async fn backup_to_file(&self, output_path: &str, dry_run: bool) -> Result<BackupReport> {
crate::maintenance::backup_to_file(self.framework, output_path, dry_run).await
}
pub async fn restore(&self, backup_path: &str, mode: ExecutionMode) -> Result<RestoreReport> {
crate::maintenance::restore_from_file(self.framework, backup_path, mode.is_dry_run()).await
}
pub async fn restore_from_file(
&self,
backup_path: &str,
dry_run: bool,
) -> Result<RestoreReport> {
crate::maintenance::restore_from_file(self.framework, backup_path, dry_run).await
}
pub async fn reset_with_mode(&self, mode: ExecutionMode) -> Result<ResetReport> {
crate::maintenance::reset_runtime_data(self.framework, mode.is_dry_run()).await
}
pub async fn reset(&self, dry_run: bool) -> Result<ResetReport> {
crate::maintenance::reset_runtime_data(self.framework, dry_run).await
}
}
pub struct MfaOperations<'a> {
pub(crate) framework: &'a AuthFramework,
}
impl MfaOperations<'_> {
pub async fn complete(&self, challenge: MfaChallenge, code: &str) -> Result<AuthToken> {
self.framework.complete_mfa(challenge, code).await
}
pub async fn complete_by_id(&self, challenge_id: &str, code: &str) -> Result<AuthToken> {
self.framework.complete_mfa_by_id(challenge_id, code).await
}
pub async fn initiate_sms(&self, user_id: &str) -> Result<String> {
self.framework.initiate_sms_challenge(user_id).await
}
pub async fn verify_sms(&self, challenge_id: &str, code: &str) -> Result<bool> {
self.framework.verify_sms_code(challenge_id, code).await
}
pub async fn initiate_email(&self, user_id: &str) -> Result<String> {
self.framework.initiate_email_challenge(user_id).await
}
pub async fn register_phone(&self, user_id: &str, phone_number: &str) -> Result<()> {
self.framework
.register_phone_number(user_id, phone_number)
.await
}
pub async fn register_email(&self, user_id: &str, email: &str) -> Result<()> {
self.framework.register_email(user_id, email).await
}
pub async fn generate_totp_secret(&self, user_id: &str) -> Result<String> {
self.framework.generate_totp_secret(user_id).await
}
pub async fn generate_totp_qr_url(
&self,
user_id: &str,
app_name: &str,
secret: &str,
) -> Result<String> {
self.framework
.generate_totp_qr_code(user_id, app_name, secret)
.await
}
pub async fn generate_totp_code(&self, secret: &str) -> Result<String> {
self.framework.generate_totp_code(secret).await
}
pub async fn verify_totp(&self, user_id: &str, code: &str) -> Result<bool> {
self.framework.verify_totp_code(user_id, code).await
}
pub async fn generate_backup_codes(&self, user_id: &str, count: usize) -> Result<Vec<String>> {
self.framework.generate_backup_codes(user_id, count).await
}
}
pub struct MonitoringOperations<'a> {
pub(crate) framework: &'a AuthFramework,
}
impl MonitoringOperations<'_> {
pub async fn health_check(
&self,
) -> Result<std::collections::HashMap<String, crate::monitoring::HealthCheckResult>> {
self.framework.health_check().await
}
pub async fn performance_metrics(&self) -> std::collections::HashMap<String, u64> {
self.framework.get_performance_metrics().await
}
pub async fn security_metrics(&self) -> Result<std::collections::HashMap<String, u64>> {
self.framework.get_security_metrics().await
}
pub async fn prometheus_metrics(&self) -> String {
self.framework.export_prometheus_metrics().await
}
pub async fn stats(&self) -> Result<AuthStats> {
self.framework.get_stats().await
}
pub async fn check_ip_rate_limit(&self, ip: &str) -> Result<bool> {
self.framework.check_ip_rate_limit(ip).await
}
pub fn manager(&self) -> Arc<crate::monitoring::MonitoringManager> {
self.framework.monitoring_manager()
}
}
pub struct AuditOperations<'a> {
pub(crate) framework: &'a AuthFramework,
}
impl AuditOperations<'_> {
pub async fn query_permission_logs(&self, query: AuditLogQuery) -> Result<Vec<String>> {
self.framework
.get_permission_audit_logs(
query.user_id.as_deref(),
query.action.as_deref(),
query.resource.as_deref(),
query.limit,
)
.await
}
pub async fn permission_logs(
&self,
user_id: Option<&str>,
action: Option<&str>,
resource: Option<&str>,
limit: Option<usize>,
) -> Result<Vec<String>> {
self.framework
.get_permission_audit_logs(user_id, action, resource, limit)
.await
}
pub async fn permission_metrics(&self) -> Result<std::collections::HashMap<String, u64>> {
self.framework.get_permission_metrics().await
}
pub async fn security_stats(&self) -> Result<SecurityAuditStats> {
self.framework.get_security_audit_stats().await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::AuthConfig;
async fn make_fw() -> AuthFramework {
let config = AuthConfig::new().secret("test_ops_secret_key_32bytes_long!".to_string());
let mut fw = AuthFramework::new(config);
fw.initialize().await.unwrap();
fw
}
#[tokio::test]
async fn test_user_ops_register_and_get() {
let fw = make_fw().await;
let uid = fw
.users()
.register("ops_user1", "ops1@test.com", "StrongP@ss1!")
.await
.unwrap();
let info = fw.users().get(&uid).await.unwrap();
assert_eq!(info.username, "ops_user1");
}
#[tokio::test]
async fn test_user_ops_list_empty() {
let fw = make_fw().await;
let list = fw.users().list_with_query(UserListQuery::new().limit(10)).await.unwrap();
assert!(list.is_empty());
}
#[tokio::test]
async fn test_user_ops_exists_by_username() {
let fw = make_fw().await;
fw.users()
.register("exists_u", "exists@test.com", "StrongP@ss1!")
.await
.unwrap();
assert!(fw.users().exists_by_username("exists_u").await.unwrap());
assert!(!fw.users().exists_by_username("nope_u").await.unwrap());
}
#[tokio::test]
async fn test_user_ops_exists_by_email() {
let fw = make_fw().await;
fw.users()
.register("email_u", "email_exists@test.com", "StrongP@ss1!")
.await
.unwrap();
assert!(
fw.users()
.exists_by_email("email_exists@test.com")
.await
.unwrap()
);
assert!(!fw.users().exists_by_email("no@test.com").await.unwrap());
}
#[tokio::test]
async fn test_user_ops_profile() {
let fw = make_fw().await;
let uid = fw
.users()
.register("profile_u", "prof@test.com", "StrongP@ss1!")
.await
.unwrap();
let profile = fw.users().profile(&uid).await.unwrap();
assert_eq!(profile.display_name().unwrap_or_default(), "profile_u");
}
#[tokio::test]
async fn test_user_ops_update_password_and_verify() {
let fw = make_fw().await;
let uid = fw
.users()
.register("pw_user", "pw@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.users()
.update_password("pw_user", "NewStr0ng!Pass")
.await
.unwrap();
assert!(
fw.users()
.verify_password(&uid, "NewStr0ng!Pass")
.await
.unwrap()
);
}
#[tokio::test]
async fn test_user_ops_update_email() {
let fw = make_fw().await;
let uid = fw
.users()
.register("email_upd_u", "old@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.users().update_email(&uid, "new@test.com").await.unwrap();
assert!(fw.users().exists_by_email("new@test.com").await.unwrap());
}
#[tokio::test]
async fn test_user_ops_set_active() {
let fw = make_fw().await;
let uid = fw
.users()
.register("act_u", "act@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.users()
.set_status(&uid, UserStatus::Inactive)
.await
.unwrap();
let info = fw.users().get(&uid).await.unwrap();
assert!(!info.active);
}
#[tokio::test]
async fn test_user_ops_delete() {
let fw = make_fw().await;
fw.users()
.register("del_u", "del@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.users().delete("del_u").await.unwrap();
assert!(!fw.users().exists_by_username("del_u").await.unwrap());
}
#[tokio::test]
async fn test_user_ops_username() {
let fw = make_fw().await;
let uid = fw
.users()
.register("name_u", "name@test.com", "StrongP@ss1!")
.await
.unwrap();
let name = fw.users().username(&uid).await.unwrap();
assert_eq!(name, "name_u");
}
#[tokio::test]
async fn test_session_ops_create_get_delete() {
let fw = make_fw().await;
let uid = fw
.users()
.register("sess_u", "sess@test.com", "StrongP@ss1!")
.await
.unwrap();
let sid = fw
.sessions()
.create(&uid, Duration::from_secs(3600), None, None)
.await
.unwrap();
let sess = fw.sessions().get(&sid).await.unwrap();
assert!(sess.is_some());
fw.sessions().delete(&sid).await.unwrap();
assert!(fw.sessions().get(&sid).await.unwrap().is_none());
}
#[tokio::test]
async fn test_session_ops_list_for_user() {
let fw = make_fw().await;
let uid = fw
.users()
.register("sl_u", "sl@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.sessions()
.create(&uid, Duration::from_secs(3600), None, None)
.await
.unwrap();
let list = fw.sessions().list_for_user(&uid).await.unwrap();
assert_eq!(list.len(), 1);
}
#[tokio::test]
async fn test_session_ops_cleanup_expired() {
let fw = make_fw().await;
fw.sessions().cleanup_expired().await.unwrap();
}
#[tokio::test]
async fn test_token_ops_create_validate_revoke() {
let fw = make_fw().await;
let uid = fw
.users()
.register("tok_u", "tok@test.com", "StrongP@ss1!")
.await
.unwrap();
let key = fw.tokens().create_api_key(&uid, None).await.unwrap();
let info = fw.tokens().validate_api_key(&key).await.unwrap();
assert!(!info.id.is_empty());
fw.tokens().revoke_api_key(&key).await.unwrap();
}
#[tokio::test]
async fn test_token_ops_api_key_lifecycle() {
let fw = make_fw().await;
let uid = fw
.users()
.register("apikey_u", "apikey@test.com", "StrongP@ss1!")
.await
.unwrap();
let key = fw.tokens().create_api_key(&uid, None).await.unwrap();
let info = fw.tokens().validate_api_key(&key).await.unwrap();
assert!(!info.id.is_empty());
fw.tokens().revoke_api_key(&key).await.unwrap();
assert!(fw.tokens().validate_api_key(&key).await.is_err());
}
#[tokio::test]
async fn test_authz_ops_grant_and_check() {
let fw = make_fw().await;
let uid = fw
.users()
.register("authz_u", "authz@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.authorization()
.grant(&uid, "read", "docs")
.await
.unwrap();
let perms = fw
.authorization()
.effective_permissions(&uid)
.await
.unwrap();
assert!(perms.iter().any(|p| p.contains("read")));
}
#[tokio::test]
async fn test_authz_ops_role_lifecycle() {
let fw = make_fw().await;
let uid = fw
.users()
.register("role_u", "role@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.authorization().assign_role(&uid, "admin").await.unwrap();
assert!(fw.authorization().has_role(&uid, "admin").await.unwrap());
let roles = fw.authorization().roles_for_user(&uid).await.unwrap();
assert!(roles.contains(&"admin".to_string()));
fw.authorization().remove_role(&uid, "admin").await.unwrap();
assert!(!fw.authorization().has_role(&uid, "admin").await.unwrap());
}
#[tokio::test]
async fn test_mfa_ops_totp_lifecycle() {
let fw = make_fw().await;
let uid = fw
.users()
.register("mfa_u", "mfa@test.com", "StrongP@ss1!")
.await
.unwrap();
let secret = fw.mfa().generate_totp_secret(&uid).await.unwrap();
assert!(!secret.is_empty());
let code = fw.mfa().generate_totp_code(&secret).await.unwrap();
assert_eq!(code.len(), 6);
assert!(fw.mfa().verify_totp(&uid, &code).await.unwrap());
}
#[tokio::test]
async fn test_mfa_ops_backup_codes() {
let fw = make_fw().await;
let uid = fw
.users()
.register("bc_u", "bc@test.com", "StrongP@ss1!")
.await
.unwrap();
let codes = fw.mfa().generate_backup_codes(&uid, 5).await.unwrap();
assert_eq!(codes.len(), 5);
}
#[tokio::test]
async fn test_mfa_ops_sms_lifecycle() {
let fw = make_fw().await;
let uid = fw
.users()
.register("smsop_u", "smsop@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.mfa().register_phone(&uid, "+12345678901").await.unwrap();
let cid = fw.mfa().initiate_sms(&uid).await.unwrap();
assert!(!cid.is_empty());
}
#[tokio::test]
async fn test_mfa_ops_email_lifecycle() {
let fw = make_fw().await;
let uid = fw
.users()
.register("emop_u", "emop@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.mfa()
.register_email(&uid, "emop@test.com")
.await
.unwrap();
let cid = fw.mfa().initiate_email(&uid).await.unwrap();
assert!(!cid.is_empty());
}
#[tokio::test]
async fn test_monitoring_ops_health_check() {
let fw = make_fw().await;
let health = fw.monitoring().health_check().await.unwrap();
assert!(!health.is_empty());
}
#[tokio::test]
async fn test_monitoring_ops_performance_metrics() {
let fw = make_fw().await;
let _metrics = fw.monitoring().performance_metrics().await;
}
#[tokio::test]
async fn test_monitoring_ops_stats() {
let fw = make_fw().await;
let stats = fw.monitoring().stats().await.unwrap();
assert_eq!(stats.active_sessions, 0);
}
#[tokio::test]
async fn test_monitoring_ops_prometheus() {
let fw = make_fw().await;
let prom = fw.monitoring().prometheus_metrics().await;
assert!(!prom.is_empty());
}
#[tokio::test]
async fn test_admin_ops_role_inheritance() {
let fw = make_fw().await;
fw.admin()
.set_role_inheritance("user", "admin")
.await
.unwrap();
}
#[tokio::test]
async fn test_admin_ops_abac_policy() {
let fw = make_fw().await;
fw.admin()
.create_abac_policy("ip_allow", "IP allowlist policy")
.await
.unwrap();
}
#[tokio::test]
async fn test_admin_ops_user_attributes() {
let fw = make_fw().await;
let uid = fw
.users()
.register("attr_u", "attr@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.admin()
.map_user_attribute(&uid, "department", "engineering")
.await
.unwrap();
let val = fw
.admin()
.get_user_attribute(&uid, "department")
.await
.unwrap();
assert_eq!(val.as_deref(), Some("engineering"));
}
#[tokio::test]
async fn test_admin_ops_delegation() {
let fw = make_fw().await;
let uid1 = fw
.users()
.register("del_from", "delf@test.com", "StrongP@ss1!")
.await
.unwrap();
let uid2 = fw
.users()
.register("del_to", "delt@test.com", "StrongP@ss1!")
.await
.unwrap();
fw.authorization()
.grant(&uid1, "write", "report")
.await
.unwrap();
fw.admin()
.delegate_permission(&uid1, &uid2, "write", "report", Duration::from_secs(3600))
.await
.unwrap();
}
#[tokio::test]
async fn test_maintenance_ops_backup_dry_run() {
let fw = make_fw().await;
let report = fw
.maintenance()
.backup_to_file("test_backup.json", true)
.await
.unwrap();
assert!(report.dry_run);
}
#[tokio::test]
async fn test_maintenance_ops_reset_dry_run() {
let fw = make_fw().await;
let report = fw.maintenance().reset(true).await.unwrap();
assert!(report.dry_run);
}
#[tokio::test]
async fn test_audit_ops_permission_logs() {
let fw = make_fw().await;
let logs = fw
.audit()
.permission_logs(None, None, None, Some(10))
.await
.unwrap();
assert!(logs.is_empty()); }
#[tokio::test]
async fn test_audit_ops_permission_metrics() {
let fw = make_fw().await;
let metrics = fw.audit().permission_metrics().await.unwrap();
assert!(!metrics.is_empty());
}
#[tokio::test]
async fn test_audit_ops_security_stats() {
let fw = make_fw().await;
let stats = fw.audit().security_stats().await.unwrap();
assert_eq!(stats.failed_logins_24h, 0);
}
}
pub struct AdminOperations<'a> {
pub(crate) framework: &'a AuthFramework,
}
impl AdminOperations<'_> {
pub async fn set_role_inheritance(&self, child_role: &str, parent_role: &str) -> Result<()> {
self.framework
.set_role_inheritance(child_role, parent_role)
.await
}
pub async fn create_abac_policy(&self, name: &str, description: &str) -> Result<()> {
self.framework.create_abac_policy(name, description).await
}
pub async fn map_user_attribute(
&self,
user_id: &str,
attribute: &str,
value: &str,
) -> Result<()> {
self.framework
.map_user_attribute(user_id, attribute, value)
.await
}
pub async fn set_user_attributes(
&self,
user_id: &str,
attributes: &[(&str, &str)],
) -> Result<()> {
for &(attribute, value) in attributes {
self.framework
.map_user_attribute(user_id, attribute, value)
.await?;
}
Ok(())
}
pub async fn get_user_attribute(
&self,
user_id: &str,
attribute: &str,
) -> Result<Option<String>> {
self.framework.get_user_attribute(user_id, attribute).await
}
pub async fn check_dynamic_permission(
&self,
user_id: &str,
action: &str,
resource: &str,
context: std::collections::HashMap<String, String>,
) -> Result<bool> {
self.framework
.check_dynamic_permission(user_id, action, resource, context)
.await
}
pub async fn check_dynamic_permission_with_context(
&self,
user_id: &str,
action: &str,
resource: &str,
context: PermissionContext,
) -> Result<bool> {
self.framework
.check_dynamic_permission(user_id, action, resource, context.into_attributes())
.await
}
pub async fn create_resource(&self, resource: &str) -> Result<()> {
self.framework.create_resource(resource).await
}
pub async fn delegate(&self, req: DelegationRequest) -> Result<()> {
self.framework
.delegate_permission(
&req.delegator_id,
&req.delegatee_id,
&req.action,
&req.resource,
req.duration,
)
.await
}
pub async fn delegate_permission(
&self,
delegator_id: &str,
delegatee_id: &str,
action: &str,
resource: &str,
duration: Duration,
) -> Result<()> {
self.framework
.delegate_permission(delegator_id, delegatee_id, action, resource, duration)
.await
}
pub async fn active_delegations(&self, user_id: &str) -> Result<Vec<String>> {
self.framework.get_active_delegations(user_id).await
}
}