auth_framework/auth_operations.rs
1//! Grouped operation facades over [`AuthFramework`].
2//!
3//! Each `*Operations` struct is a lightweight view (holds only a lifetime-bound
4//! reference to the framework) that exposes a focused subset of the full API.
5//! They are created by the corresponding accessor on `AuthFramework` (e.g. [`users()`]).
6//!
7//! # Request structs
8//!
9//! Several operations accept structured request types instead of long
10//! parameter lists. These types carry required parameters via a
11//! constructor and optional parameters via builder methods:
12//!
13//! - [`SessionCreateRequest`] — session creation with optional IP / user-agent.
14//! - [`AuditLogQuery`] — filtered audit log queries.
15//! - [`UserListQuery`] — user listing with pagination and filtering.
16//! - [`PermissionContext`] — context data for dynamic permission evaluation.
17//! - [`DelegationRequest`] — permission delegation with required fields.
18//! - [`ExecutionMode`] — `DryRun` vs `Execute` for maintenance operations.
19//! - [`UserStatus`] — `Active` vs `Inactive` for user account state.
20//! - [`SessionFilter`] — `ActiveOnly` vs `IncludeInactive` for session listing.
21//!
22//! [`users()`]: crate::auth::AuthFramework::users
23
24use crate::audit::SecurityAuditStats;
25use crate::auth::{AuthFramework, AuthStats, UserInfo};
26use crate::errors::{AuthError, Result};
27use crate::maintenance::{BackupReport, ResetReport, RestoreReport};
28use crate::methods::MfaChallenge;
29use crate::permissions::Role;
30use crate::storage::SessionData;
31use crate::tokens::AuthToken;
32use std::sync::Arc;
33use std::time::Duration;
34
35/// Controls whether a maintenance operation (backup, restore, reset) actually
36/// modifies state or merely previews what *would* happen.
37///
38/// Replaces bare `bool` parameters for better call-site readability.
39///
40/// # Example
41///
42/// ```rust,ignore
43/// use auth_framework::auth_operations::ExecutionMode;
44///
45/// // Preview:
46/// let preview = auth.maintenance().backup("backup.json", ExecutionMode::DryRun).await?;
47/// // For real:
48/// let report = auth.maintenance().backup("backup.json", ExecutionMode::Execute).await?;
49/// ```
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub enum ExecutionMode {
52 /// Report what the operation would do without making changes.
53 DryRun,
54 /// Actually perform the operation.
55 Execute,
56}
57
58impl ExecutionMode {
59 /// Returns `true` when this is a dry-run (preview) execution.
60 pub fn is_dry_run(self) -> bool {
61 matches!(self, Self::DryRun)
62 }
63}
64
65impl From<ExecutionMode> for bool {
66 /// Converts to the legacy `dry_run: bool` parameter (`DryRun → true`).
67 fn from(mode: ExecutionMode) -> bool {
68 mode.is_dry_run()
69 }
70}
71
72/// Whether a user account should be active (able to log in) or inactive
73/// (locked out).
74///
75/// Replaces bare `bool` parameters for better call-site readability.
76///
77/// # Example
78///
79/// ```rust,ignore
80/// use auth_framework::auth_operations::UserStatus;
81///
82/// // Deactivate a user:
83/// auth.users().set_status(&user_id, UserStatus::Inactive).await?;
84/// // Re-activate:
85/// auth.users().set_status(&user_id, UserStatus::Active).await?;
86/// ```
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
88pub enum UserStatus {
89 /// Account is active and can authenticate.
90 Active,
91 /// Account is disabled and all login attempts will be rejected.
92 Inactive,
93}
94
95impl UserStatus {
96 /// Returns `true` when the user should be active.
97 pub fn is_active(self) -> bool {
98 matches!(self, Self::Active)
99 }
100}
101
102impl From<UserStatus> for bool {
103 /// Converts to the legacy `active: bool` parameter (`Active → true`).
104 fn from(status: UserStatus) -> bool {
105 status.is_active()
106 }
107}
108
109impl From<bool> for UserStatus {
110 fn from(active: bool) -> Self {
111 if active { Self::Active } else { Self::Inactive }
112 }
113}
114
115/// Filter for session listing queries.
116///
117/// Replaces `include_inactive: bool` for self-documenting call sites.
118///
119/// # Example
120///
121/// ```rust,ignore
122/// use auth_framework::auth_operations::SessionFilter;
123///
124/// let active = mgr.get_user_sessions(user_id, SessionFilter::ActiveOnly).await?;
125/// let all = mgr.get_user_sessions(user_id, SessionFilter::IncludeInactive).await?;
126/// ```
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
128pub enum SessionFilter {
129 /// Return only active sessions.
130 ActiveOnly,
131 /// Return all sessions including expired/revoked ones.
132 IncludeInactive,
133}
134
135impl SessionFilter {
136 /// Returns `true` when inactive sessions should be included.
137 pub fn include_inactive(self) -> bool {
138 matches!(self, Self::IncludeInactive)
139 }
140}
141
142impl From<SessionFilter> for bool {
143 /// Converts to the legacy `include_inactive: bool` parameter.
144 fn from(filter: SessionFilter) -> bool {
145 filter.include_inactive()
146 }
147}
148
149// ──────────────────────────────────────────────────────────────────────────────
150// Request / query structs
151// ──────────────────────────────────────────────────────────────────────────────
152
153/// Filtering criteria for [`AuditOperations::query_permission_logs`].
154///
155/// All fields are optional — an empty query returns unfiltered results.
156///
157/// # Example
158///
159/// ```rust,ignore
160/// let query = AuditLogQuery::new()
161/// .user("user_123")
162/// .action("read")
163/// .limit(50);
164/// ```
165#[derive(Debug, Clone, Default)]
166pub struct AuditLogQuery {
167 user_id: Option<String>,
168 action: Option<String>,
169 resource: Option<String>,
170 limit: Option<usize>,
171}
172
173impl AuditLogQuery {
174 /// Create an empty query (matches everything).
175 pub fn new() -> Self {
176 Self::default()
177 }
178
179 /// Filter by user ID.
180 pub fn user(mut self, user_id: impl Into<String>) -> Self {
181 self.user_id = Some(user_id.into());
182 self
183 }
184
185 /// Filter by action (e.g. `"read"`, `"write"`).
186 pub fn action(mut self, action: impl Into<String>) -> Self {
187 self.action = Some(action.into());
188 self
189 }
190
191 /// Filter by resource path.
192 pub fn resource(mut self, resource: impl Into<String>) -> Self {
193 self.resource = Some(resource.into());
194 self
195 }
196
197 /// Limit the number of returned entries.
198 pub fn limit(mut self, limit: usize) -> Self {
199 self.limit = Some(limit);
200 self
201 }
202
203 /// Returns the user-ID filter, if set.
204 pub fn get_user_id(&self) -> Option<&str> {
205 self.user_id.as_deref()
206 }
207
208 /// Returns the action filter, if set.
209 pub fn get_action(&self) -> Option<&str> {
210 self.action.as_deref()
211 }
212
213 /// Returns the resource filter, if set.
214 pub fn get_resource(&self) -> Option<&str> {
215 self.resource.as_deref()
216 }
217
218 /// Returns the maximum number of entries to return, if set.
219 pub fn get_limit(&self) -> Option<usize> {
220 self.limit
221 }
222}
223
224/// Query type for listing users with pagination and filtering.
225///
226/// Provides a fluent API for configuring user listing parameters.
227///
228/// # Example
229///
230/// ```rust
231/// # use auth_framework::auth_operations::UserListQuery;
232/// let query = UserListQuery::new()
233/// .limit(50)
234/// .active_only();
235/// ```
236#[derive(Debug, Clone, Default)]
237pub struct UserListQuery {
238 limit: Option<usize>,
239 offset: Option<usize>,
240 active_only: bool,
241}
242
243impl UserListQuery {
244 /// Create an empty query (returns all users).
245 pub fn new() -> Self {
246 Self::default()
247 }
248
249 /// Limit the number of users returned.
250 pub fn limit(mut self, limit: usize) -> Self {
251 self.limit = Some(limit);
252 self
253 }
254
255 /// Skip the first N users (for pagination).
256 pub fn offset(mut self, offset: usize) -> Self {
257 self.offset = Some(offset);
258 self
259 }
260
261 /// Only return active users (default: false).
262 pub fn active_only(mut self) -> Self {
263 self.active_only = true;
264 self
265 }
266
267 /// Conditionally restrict the query to active users.
268 pub fn active_only_if(mut self, active_only: bool) -> Self {
269 self.active_only = active_only;
270 self
271 }
272
273 /// Conditionally apply a limit when one is provided.
274 pub fn limit_if_some(mut self, limit: Option<usize>) -> Self {
275 if let Some(limit) = limit {
276 self.limit = Some(limit);
277 }
278 self
279 }
280
281 /// Returns the maximum number of users to return, if set.
282 pub fn get_limit(&self) -> Option<usize> {
283 self.limit
284 }
285
286 /// Returns the pagination offset, if set.
287 pub fn get_offset(&self) -> Option<usize> {
288 self.offset
289 }
290
291 /// Returns `true` when only active users should be listed.
292 pub fn get_active_only(&self) -> bool {
293 self.active_only
294 }
295}
296
297/// Context data for dynamic permission evaluation (ABAC).
298///
299/// Provides a structured way to pass environmental and contextual information
300/// for attribute-based access control decisions.
301///
302/// # Example
303///
304/// ```rust
305/// # use auth_framework::auth_operations::PermissionContext;
306/// let context = PermissionContext::new()
307/// .with_attribute("time_of_day", "business_hours")
308/// .with_attribute("ip_location", "office")
309/// .with_attribute("device_type", "trusted");
310/// ```
311#[derive(Debug, Clone, Default)]
312pub struct PermissionContext {
313 attributes: std::collections::HashMap<String, String>,
314}
315
316impl PermissionContext {
317 /// Create an empty context.
318 pub fn new() -> Self {
319 Self::default()
320 }
321
322 /// Add a context attribute.
323 pub fn with_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
324 self.attributes.insert(key.into(), value.into());
325 self
326 }
327
328 /// Add multiple attributes from an iterator.
329 pub fn with_attributes<I, K, V>(mut self, attributes: I) -> Self
330 where
331 I: IntoIterator<Item = (K, V)>,
332 K: Into<String>,
333 V: Into<String>,
334 {
335 for (key, value) in attributes {
336 self.attributes.insert(key.into(), value.into());
337 }
338 self
339 }
340
341 /// Get the underlying attributes map.
342 pub fn into_attributes(self) -> std::collections::HashMap<String, String> {
343 self.attributes
344 }
345
346 /// Get a reference to the attributes map.
347 pub fn attributes(&self) -> &std::collections::HashMap<String, String> {
348 &self.attributes
349 }
350}
351
352/// Request type for [`AdminOperations::delegate`].
353///
354/// Bundles the four required delegation fields and the optional duration
355/// into a single self-documenting value.
356///
357/// # Example
358///
359/// ```rust,ignore
360/// let req = DelegationRequest::new("admin_1", "user_2", "write", "reports")
361/// .duration(Duration::from_secs(3600));
362/// ```
363#[derive(Debug, Clone)]
364pub struct DelegationRequest {
365 delegator_id: String,
366 delegatee_id: String,
367 action: String,
368 resource: String,
369 duration: Duration,
370}
371
372impl DelegationRequest {
373 /// Create a delegation request with required parameters.
374 ///
375 /// The default duration is 1 hour. Override with [`duration`](Self::duration).
376 pub fn new(
377 delegator_id: impl Into<String>,
378 delegatee_id: impl Into<String>,
379 action: impl Into<String>,
380 resource: impl Into<String>,
381 ) -> Self {
382 Self {
383 delegator_id: delegator_id.into(),
384 delegatee_id: delegatee_id.into(),
385 action: action.into(),
386 resource: resource.into(),
387 duration: Duration::from_secs(3600),
388 }
389 }
390
391 /// Set the delegation duration (replaces the default of 1 hour).
392 pub fn duration(mut self, duration: Duration) -> Self {
393 self.duration = duration;
394 self
395 }
396
397 /// Returns the ID of the user granting permissions.
398 pub fn delegator_id(&self) -> &str {
399 &self.delegator_id
400 }
401
402 /// Returns the ID of the user receiving permissions.
403 pub fn delegatee_id(&self) -> &str {
404 &self.delegatee_id
405 }
406
407 /// Returns the action being delegated (e.g. `"write"`).
408 pub fn action(&self) -> &str {
409 &self.action
410 }
411
412 /// Returns the resource being delegated (e.g. `"reports"`).
413 pub fn resource(&self) -> &str {
414 &self.resource
415 }
416
417 /// Returns how long the delegation is valid.
418 pub fn get_duration(&self) -> Duration {
419 self.duration
420 }
421}
422
423// ──────────────────────────────────────────────────────────────────────────────
424// User operations
425// ──────────────────────────────────────────────────────────────────────────────
426
427/// Focused user-management operations exposed from [`AuthFramework::users`].
428///
429/// # Example
430///
431/// ```rust,no_run
432/// # use auth_framework::prelude::*;
433/// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
434/// // Register
435/// let uid = auth.users().register("alice", "alice@example.com", "P@ssw0rd!").await?;
436///
437/// // Look up
438/// let user = auth.users().get(&uid).await?;
439/// assert_eq!(user.username, "alice");
440///
441/// // Update password
442/// auth.users().update_password_by_id(&uid, "NewP@ss!").await?;
443/// # Ok(())
444/// # }
445/// ```
446pub struct UserOperations<'a> {
447 pub(crate) framework: &'a AuthFramework,
448}
449
450impl UserOperations<'_> {
451 /// Register a new user and return the generated user ID.
452 pub async fn register(&self, username: &str, email: &str, password: &str) -> Result<String> {
453 self.framework
454 .register_user(username, email, password)
455 .await
456 }
457
458 /// List users from the canonical user index.
459 ///
460 /// **Prefer [`list_with_query`](Self::list_with_query)** which uses a
461 /// [`UserListQuery`] builder for better readability at call sites.
462 #[deprecated(
463 since = "0.6.0",
464 note = "use `list_with_query(UserListQuery::new().limit(n).active_only())` instead"
465 )]
466 pub async fn list(
467 &self,
468 limit: Option<usize>,
469 offset: Option<usize>,
470 active_only: bool,
471 ) -> Result<Vec<UserInfo>> {
472 self.framework
473 .list_users_with_query(
474 UserListQuery::new()
475 .offset(offset.unwrap_or(0))
476 .active_only_if(active_only)
477 .limit_if_some(limit),
478 )
479 .await
480 }
481
482 /// List users using a [`UserListQuery`] for better readability.
483 ///
484 /// # Example
485 ///
486 /// ```rust,no_run
487 /// # use auth_framework::prelude::*;
488 /// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
489 /// let active = auth.users()
490 /// .list_with_query(UserListQuery::new().limit(50).active_only())
491 /// .await?;
492 /// # Ok(())
493 /// # }
494 /// ```
495 pub async fn list_with_query(&self, query: UserListQuery) -> Result<Vec<UserInfo>> {
496 self.framework
497 .list_users_with_query(query)
498 .await
499 }
500
501 /// Fetch a user record by canonical user ID.
502 pub async fn get(&self, user_id: &str) -> Result<UserInfo> {
503 self.framework.get_user_record(user_id).await
504 }
505
506 /// Check whether a username exists.
507 pub async fn exists_by_username(&self, username: &str) -> Result<bool> {
508 self.framework.username_exists(username).await
509 }
510
511 /// Check whether an email exists.
512 pub async fn exists_by_email(&self, email: &str) -> Result<bool> {
513 self.framework.email_exists(email).await
514 }
515
516 /// Fetch a user record by username.
517 pub async fn get_by_username(
518 &self,
519 username: &str,
520 ) -> Result<std::collections::HashMap<String, serde_json::Value>> {
521 self.framework.get_user_by_username(username).await
522 }
523
524 /// Fetch an application-level user profile.
525 pub async fn profile(&self, user_id: &str) -> Result<crate::providers::ProviderProfile> {
526 self.framework.get_user_profile(user_id).await
527 }
528
529 /// Update a user's password.
530 pub async fn update_password(&self, username: &str, new_password: &str) -> Result<()> {
531 self.framework
532 .update_user_password(username, new_password)
533 .await
534 }
535
536 /// Update a user's password by user ID.
537 pub async fn update_password_by_id(&self, user_id: &str, new_password: &str) -> Result<()> {
538 self.framework
539 .update_user_password_by_id(user_id, new_password)
540 .await
541 }
542
543 /// Update the roles assigned to a user.
544 pub async fn update_roles(&self, user_id: &str, roles: &[String]) -> Result<()> {
545 self.framework.update_user_roles(user_id, roles).await
546 }
547
548 /// Enable or disable a user.
549 #[deprecated(since = "0.5.0", note = "use `set_status(id, UserStatus)` instead")]
550 pub async fn set_active(&self, user_id: &str, active: bool) -> Result<()> {
551 self.framework.set_user_active(user_id, active).await
552 }
553
554 /// Change whether a user account is active or inactive.
555 ///
556 /// Inactive users cannot authenticate until re-activated.
557 ///
558 /// # Example
559 ///
560 /// ```rust,ignore
561 /// auth.users().set_status(&user_id, UserStatus::Inactive).await?;
562 /// ```
563 pub async fn set_status(&self, user_id: &str, status: UserStatus) -> Result<()> {
564 self.framework
565 .set_user_active(user_id, status.is_active())
566 .await
567 }
568
569 /// Update a user's email address.
570 pub async fn update_email(&self, user_id: &str, email: &str) -> Result<()> {
571 self.framework.update_user_email(user_id, email).await
572 }
573
574 /// Verify a user's password.
575 pub async fn verify_password(&self, user_id: &str, password: &str) -> Result<bool> {
576 self.framework.verify_user_password(user_id, password).await
577 }
578
579 /// Resolve a username from a user ID.
580 pub async fn username(&self, user_id: &str) -> Result<String> {
581 self.framework.get_username_by_id(user_id).await
582 }
583
584 /// Delete a user by username.
585 pub async fn delete(&self, username: &str) -> Result<()> {
586 self.framework.delete_user(username).await
587 }
588
589 /// Delete a user by user ID.
590 pub async fn delete_by_id(&self, user_id: &str) -> Result<()> {
591 self.framework.delete_user_by_id(user_id).await
592 }
593
594 /// Validate a username against the configured format rules.
595 ///
596 /// Returns `Ok(())` when the username is acceptable, or
597 /// `Err(AuthError::Validation { .. })` describing the policy violation.
598 ///
599 /// # Example
600 ///
601 /// ```rust,no_run
602 /// # use auth_framework::prelude::*;
603 /// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
604 /// auth.users().check_username("alice")?;
605 /// // Returns Err explaining why the name is invalid:
606 /// assert!(auth.users().check_username("").is_err());
607 /// # Ok(())
608 /// # }
609 /// ```
610 pub fn check_username(&self, username: &str) -> Result<()> {
611 crate::utils::validation::validate_username(username)
612 }
613
614 /// Validate a password against the active security policy.
615 ///
616 /// Returns `Ok(())` when the password meets production strength
617 /// requirements, or `Err(AuthError::Validation { .. })` with feedback.
618 ///
619 /// # Example
620 ///
621 /// ```rust,no_run
622 /// # use auth_framework::prelude::*;
623 /// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
624 /// auth.users().check_password_strength("C0mpl3x!Pa$$word")?;
625 /// assert!(auth.users().check_password_strength("weak").is_err());
626 /// # Ok(())
627 /// # }
628 /// ```
629 pub fn check_password_strength(&self, password: &str) -> Result<()> {
630 let strength = crate::utils::password::check_password_strength(password);
631 if crate::utils::password::meets_production_strength(strength.level) {
632 Ok(())
633 } else {
634 Err(AuthError::validation(format!(
635 "Password does not meet strength requirements: {}",
636 strength.feedback.join(", ")
637 )))
638 }
639 }
640
641 /// Validate an email address against RFC 5322 format rules.
642 ///
643 /// Returns `Ok(())` when the email is acceptable, or
644 /// `Err(AuthError::Validation { .. })` describing the issue.
645 ///
646 /// # Example
647 ///
648 /// ```rust,no_run
649 /// # use auth_framework::prelude::*;
650 /// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
651 /// auth.users().check_email("alice@example.com")?;
652 /// assert!(auth.users().check_email("not-an-email").is_err());
653 /// # Ok(())
654 /// # }
655 /// ```
656 pub fn check_email(&self, email: &str) -> Result<()> {
657 crate::utils::validation::validate_email(email)
658 }
659}
660
661// ──────────────────────────────────────────────────────────────────────────────
662// Session operations
663// ──────────────────────────────────────────────────────────────────────────────
664
665/// Request type for [`SessionOperations::create`].
666///
667/// Bundles the required session parameters (user ID, lifetime) with optional
668/// context (IP address, user-agent) so callers never need to pass `None`
669/// explicitly.
670///
671/// # Example
672///
673/// ```rust,no_run
674/// # use auth_framework::auth_operations::SessionCreateRequest;
675/// # use std::time::Duration;
676/// // Minimal — no optional fields:
677/// let req = SessionCreateRequest::new("user-123", Duration::from_secs(3600));
678///
679/// // With optional context:
680/// let req = SessionCreateRequest::new("user-123", Duration::from_secs(3600))
681/// .ip_address("10.0.0.1")
682/// .user_agent("Mozilla/5.0");
683/// ```
684#[derive(Debug, Clone)]
685pub struct SessionCreateRequest {
686 user_id: String,
687 expires_in: Duration,
688 ip_address: Option<String>,
689 user_agent: Option<String>,
690}
691
692impl SessionCreateRequest {
693 /// Create a request with the required fields.
694 pub fn new(user_id: impl Into<String>, expires_in: Duration) -> Self {
695 Self {
696 user_id: user_id.into(),
697 expires_in,
698 ip_address: None,
699 user_agent: None,
700 }
701 }
702
703 /// Attach a client IP address for audit / geolocation.
704 pub fn ip_address(mut self, ip: impl Into<String>) -> Self {
705 self.ip_address = Some(ip.into());
706 self
707 }
708
709 /// Attach a `User-Agent` string for device tracking.
710 pub fn user_agent(mut self, ua: impl Into<String>) -> Self {
711 self.user_agent = Some(ua.into());
712 self
713 }
714
715 /// Returns the user ID this session is being created for.
716 pub fn get_user_id(&self) -> &str {
717 &self.user_id
718 }
719
720 /// Returns how long until the session expires.
721 pub fn get_expires_in(&self) -> Duration {
722 self.expires_in
723 }
724
725 /// Returns the client IP address, if attached.
726 pub fn get_ip_address(&self) -> Option<&str> {
727 self.ip_address.as_deref()
728 }
729
730 /// Returns the `User-Agent` header value, if attached.
731 pub fn get_user_agent(&self) -> Option<&str> {
732 self.user_agent.as_deref()
733 }
734}
735
736/// Focused session-management operations exposed from [`AuthFramework::sessions`].
737///
738/// # Example
739///
740/// ```rust,no_run
741/// # use auth_framework::prelude::*;
742/// # use auth_framework::auth_operations::SessionCreateRequest;
743/// # use std::time::Duration;
744/// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
745/// // Using the request struct (recommended):
746/// let req = SessionCreateRequest::new("user-123", Duration::from_secs(3600))
747/// .ip_address("10.0.0.1");
748/// let sid = auth.sessions().create_session(req).await?;
749///
750/// // Positional shorthand still works:
751/// let sid = auth.sessions().create("user-123", Duration::from_secs(3600), None, None).await?;
752///
753/// let session = auth.sessions().get(&sid).await?;
754/// auth.sessions().delete(&sid).await?;
755/// # Ok(())
756/// # }
757/// ```
758pub struct SessionOperations<'a> {
759 pub(crate) framework: &'a AuthFramework,
760}
761
762impl SessionOperations<'_> {
763 /// Create a new session from a [`SessionCreateRequest`].
764 ///
765 /// This is the preferred entry point — it avoids passing `None` for
766 /// optional parameters and makes the call site self-documenting.
767 pub async fn create_session(&self, req: SessionCreateRequest) -> Result<String> {
768 self.framework
769 .create_session(&req.user_id, req.expires_in, req.ip_address, req.user_agent)
770 .await
771 }
772
773 /// Create a new session for a user (positional convenience).
774 ///
775 /// Prefer [`create_session`](Self::create_session) with a
776 /// [`SessionCreateRequest`] when you need optional fields.
777 pub async fn create(
778 &self,
779 user_id: &str,
780 expires_in: Duration,
781 ip_address: Option<String>,
782 user_agent: Option<String>,
783 ) -> Result<String> {
784 self.framework
785 .create_session(user_id, expires_in, ip_address, user_agent)
786 .await
787 }
788
789 /// Fetch a session by ID.
790 pub async fn get(&self, session_id: &str) -> Result<Option<SessionData>> {
791 self.framework.get_session(session_id).await
792 }
793
794 /// Delete a session by ID.
795 pub async fn delete(&self, session_id: &str) -> Result<()> {
796 self.framework.delete_session(session_id).await
797 }
798
799 /// List all sessions owned by a user.
800 pub async fn list_for_user(&self, user_id: &str) -> Result<Vec<SessionData>> {
801 self.framework.storage().list_user_sessions(user_id).await
802 }
803
804 /// List sessions owned by a user, optionally filtering out expired ones.
805 ///
806 /// # Example
807 ///
808 /// ```rust,no_run
809 /// # use auth_framework::prelude::*;
810 /// # use auth_framework::auth_operations::SessionFilter;
811 /// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
812 /// let active = auth.sessions()
813 /// .list_for_user_filtered("user-1", SessionFilter::ActiveOnly)
814 /// .await?;
815 /// # Ok(())
816 /// # }
817 /// ```
818 pub async fn list_for_user_filtered(
819 &self,
820 user_id: &str,
821 filter: SessionFilter,
822 ) -> Result<Vec<SessionData>> {
823 let sessions = self.framework.storage().list_user_sessions(user_id).await?;
824 if filter.include_inactive() {
825 Ok(sessions)
826 } else {
827 Ok(sessions.into_iter().filter(|s| !s.is_expired()).collect())
828 }
829 }
830
831 /// Remove expired sessions and tokens.
832 pub async fn cleanup_expired(&self) -> Result<()> {
833 self.framework.cleanup_expired_data().await
834 }
835}
836
837// ──────────────────────────────────────────────────────────────────────────────
838// Token operations
839// ──────────────────────────────────────────────────────────────────────────────
840
841/// Builder for [`TokenOperations::create_token`].
842///
843/// Bundles the required token parameters (user ID, auth method) with optional
844/// scopes and lifetime, keeping call sites readable.
845///
846/// # Example
847///
848/// ```rust,no_run
849/// # use auth_framework::prelude::*;
850/// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
851/// let token = auth.tokens().create_token(
852/// TokenCreateRequest::new("user-123", "jwt")
853/// .scope("read")
854/// .scope("write")
855/// .lifetime(std::time::Duration::from_secs(7200))
856/// ).await?;
857/// # Ok(())
858/// # }
859/// ```
860#[derive(Debug, Clone)]
861pub struct TokenCreateRequest {
862 user_id: String,
863 method: String,
864 scopes: Vec<String>,
865 lifetime: Option<Duration>,
866}
867
868impl TokenCreateRequest {
869 /// Create a new token request for a user authenticated via `method`.
870 pub fn new(user_id: impl Into<String>, method: impl Into<String>) -> Self {
871 Self {
872 user_id: user_id.into(),
873 method: method.into(),
874 scopes: Vec::new(),
875 lifetime: None,
876 }
877 }
878
879 /// Add a single scope.
880 pub fn scope(mut self, scope: impl Into<String>) -> Self {
881 self.scopes.push(scope.into());
882 self
883 }
884
885 /// Add multiple scopes at once.
886 pub fn scopes<I, S>(mut self, scopes: I) -> Self
887 where
888 I: IntoIterator<Item = S>,
889 S: Into<String>,
890 {
891 self.scopes.extend(scopes.into_iter().map(Into::into));
892 self
893 }
894
895 /// Override the default token lifetime.
896 pub fn lifetime(mut self, duration: Duration) -> Self {
897 self.lifetime = Some(duration);
898 self
899 }
900}
901
902/// Focused token-management operations exposed from [`AuthFramework::tokens`].
903///
904/// # Example
905///
906/// ```rust,no_run
907/// # use auth_framework::prelude::*;
908/// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
909/// // Issue a JWT token
910/// let token = auth.tokens().create("user-123", &["read"], "jwt", None).await?;
911///
912/// // Validate
913/// assert!(auth.tokens().validate(&token).await?);
914///
915/// // Refresh
916/// let new_token = auth.tokens().refresh(&token).await?;
917///
918/// // Revoke
919/// auth.tokens().revoke(&new_token).await?;
920/// # Ok(())
921/// # }
922/// ```
923pub struct TokenOperations<'a> {
924 pub(crate) framework: &'a AuthFramework,
925}
926
927impl TokenOperations<'_> {
928 /// Create a new authentication token for a user.
929 ///
930 /// `scopes` accepts any iterator of string-like values, so all of these
931 /// work:
932 ///
933 /// ```rust,ignore
934 /// // Vec<String>
935 /// tokens.create("uid", vec!["read".into()], "jwt", None).await?;
936 /// // Slice of &str
937 /// tokens.create("uid", &["read", "write"], "jwt", None).await?;
938 /// // Empty
939 /// tokens.create("uid", std::iter::empty::<&str>(), "jwt", None).await?;
940 /// ```
941 ///
942 /// # Arguments
943 ///
944 /// * `user_id` — the user to issue a token for
945 /// * `scopes` — permission scopes to embed in the token
946 /// * `method_name` — the auth method that authenticated the user (e.g. `"jwt"`)
947 /// * `lifetime` — custom lifetime, or `None` for the configured default
948 pub async fn create<I, S>(
949 &self,
950 user_id: impl Into<String>,
951 scopes: I,
952 method_name: impl Into<String>,
953 lifetime: Option<Duration>,
954 ) -> Result<AuthToken>
955 where
956 I: IntoIterator<Item = S>,
957 S: AsRef<str>,
958 {
959 let scopes: Vec<String> = scopes.into_iter().map(|s| s.as_ref().to_owned()).collect();
960 self.framework
961 .create_auth_token(user_id, scopes, method_name, lifetime)
962 .await
963 }
964
965 /// Create a token from a [`TokenCreateRequest`].
966 ///
967 /// This is the preferred entry point — it replaces positional `Option`
968 /// parameters with a self-documenting builder.
969 ///
970 /// # Example
971 ///
972 /// ```rust,no_run
973 /// # use auth_framework::prelude::*;
974 /// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
975 /// let token = auth.tokens().create_token(
976 /// TokenCreateRequest::new("user-123", "jwt")
977 /// .scope("read")
978 /// .scope("write")
979 /// ).await?;
980 /// # Ok(())
981 /// # }
982 /// ```
983 pub async fn create_token(&self, req: TokenCreateRequest) -> Result<AuthToken> {
984 self.framework
985 .create_auth_token(req.user_id, req.scopes, req.method, req.lifetime)
986 .await
987 }
988
989 /// Validate an authentication token.
990 pub async fn validate(&self, token: &AuthToken) -> Result<bool> {
991 self.framework.validate_token(token).await
992 }
993
994 /// Refresh an authentication token.
995 pub async fn refresh(&self, token: &AuthToken) -> Result<AuthToken> {
996 self.framework.refresh_token(token).await
997 }
998
999 /// Revoke an authentication token.
1000 pub async fn revoke(&self, token: &AuthToken) -> Result<()> {
1001 self.framework.revoke_token(token).await
1002 }
1003
1004 /// List all tokens belonging to a user.
1005 pub async fn list_for_user(&self, user_id: &str) -> Result<Vec<AuthToken>> {
1006 self.framework.list_user_tokens(user_id).await
1007 }
1008
1009 /// Create an API key for a user.
1010 pub async fn create_api_key(
1011 &self,
1012 user_id: &str,
1013 expires_in: Option<Duration>,
1014 ) -> Result<String> {
1015 self.framework.create_api_key(user_id, expires_in).await
1016 }
1017
1018 /// Validate an API key and return the associated user info.
1019 pub async fn validate_api_key(&self, api_key: &str) -> Result<UserInfo> {
1020 self.framework.validate_api_key(api_key).await
1021 }
1022
1023 /// Revoke an API key.
1024 pub async fn revoke_api_key(&self, api_key: &str) -> Result<()> {
1025 self.framework.revoke_api_key(api_key).await
1026 }
1027}
1028
1029// ──────────────────────────────────────────────────────────────────────────────
1030// Authorization operations
1031// ──────────────────────────────────────────────────────────────────────────────
1032
1033/// Focused authorization operations exposed via [`AuthFramework::authorization()`].
1034///
1035/// Provides role-based access control (RBAC), direct permission grants, and
1036/// effective-permission queries.
1037///
1038/// # Example
1039///
1040/// ```rust,no_run
1041/// # async fn example(auth: &auth_framework::AuthFramework) -> auth_framework::Result<()> {
1042/// use auth_framework::permissions::{Permission, Role};
1043/// use auth_framework::tokens::AuthToken;
1044///
1045/// let authz = auth.authorization();
1046/// let token = AuthToken::builder("token_123", "user_123", "access_token").build();
1047///
1048/// // Create a role and assign it to a user
1049/// let mut editor = Role::new("editor");
1050/// editor.add_permission(Permission::new("articles", "edit"));
1051/// authz.create_role(editor).await?;
1052/// authz.assign_role("user_123", "editor").await?;
1053///
1054/// // Check permission via token
1055/// let allowed = authz.check(&token, "edit", "articles").await?;
1056/// # Ok(())
1057/// # }
1058/// ```
1059pub struct AuthorizationOperations<'a> {
1060 pub(crate) framework: &'a AuthFramework,
1061}
1062
1063impl AuthorizationOperations<'_> {
1064 /// Check whether a token grants access to an action on a resource.
1065 pub async fn check(&self, token: &AuthToken, action: &str, resource: &str) -> Result<bool> {
1066 self.framework
1067 .check_permission(token, action, resource)
1068 .await
1069 }
1070
1071 /// Grant a direct permission to a user.
1072 pub async fn grant(&self, user_id: &str, action: &str, resource: &str) -> Result<()> {
1073 self.framework
1074 .grant_permission(user_id, action, resource)
1075 .await
1076 }
1077
1078 /// Revoke a direct permission from a user.
1079 pub async fn revoke(&self, user_id: &str, action: &str, resource: &str) -> Result<()> {
1080 self.framework
1081 .revoke_permission(user_id, action, resource)
1082 .await
1083 }
1084
1085 /// Create a role.
1086 pub async fn create_role(&self, role: Role) -> Result<()> {
1087 self.framework.create_role(role).await
1088 }
1089
1090 /// List all defined roles.
1091 pub async fn list_roles(&self) -> Vec<Role> {
1092 self.framework.list_roles().await
1093 }
1094
1095 /// Fetch a role definition by name.
1096 pub async fn role(&self, role_name: &str) -> Result<Role> {
1097 self.framework.get_role(role_name).await
1098 }
1099
1100 /// Add a permission to an existing role.
1101 pub async fn add_role_permission(
1102 &self,
1103 role_name: &str,
1104 permission: crate::permissions::Permission,
1105 ) -> Result<()> {
1106 self.framework
1107 .add_role_permission(role_name, permission)
1108 .await
1109 }
1110
1111 /// Assign a role to a user.
1112 pub async fn assign_role(&self, user_id: &str, role_name: &str) -> Result<()> {
1113 self.framework.assign_role(user_id, role_name).await
1114 }
1115
1116 /// Remove a role from a user.
1117 pub async fn remove_role(&self, user_id: &str, role_name: &str) -> Result<()> {
1118 self.framework.remove_role(user_id, role_name).await
1119 }
1120
1121 /// Check whether a user currently has a role.
1122 pub async fn has_role(&self, user_id: &str, role_name: &str) -> Result<bool> {
1123 self.framework.user_has_role(user_id, role_name).await
1124 }
1125
1126 /// List effective permissions for a user.
1127 pub async fn effective_permissions(&self, user_id: &str) -> Result<crate::types::Permissions> {
1128 self.framework
1129 .get_effective_permissions(user_id)
1130 .await
1131 .map(crate::types::Permissions)
1132 }
1133
1134 /// List the currently assigned runtime roles for a user.
1135 pub async fn roles_for_user(&self, user_id: &str) -> Result<crate::types::Roles> {
1136 self.framework
1137 .list_user_roles(user_id)
1138 .await
1139 .map(crate::types::Roles)
1140 }
1141}
1142
1143/// Focused maintenance operations exposed via [`AuthFramework::maintenance()`].
1144///
1145/// Backup, restore, and reset authentication state in the configured storage
1146/// backend. All operations support an [`ExecutionMode`] parameter that
1147/// controls whether changes are applied or merely previewed.
1148///
1149/// # Example
1150///
1151/// ```rust,no_run
1152/// # use auth_framework::auth_operations::ExecutionMode;
1153/// # async fn example(auth: &auth_framework::AuthFramework) -> auth_framework::Result<()> {
1154/// let maint = auth.maintenance();
1155///
1156/// // Preview a backup without writing to disk
1157/// let preview = maint.backup("/tmp/auth_backup.json", ExecutionMode::DryRun).await?;
1158/// println!("Would export {} records", preview.manifest.user_count);
1159///
1160/// // Perform the real backup
1161/// let report = maint.backup("/tmp/auth_backup.json", ExecutionMode::Execute).await?;
1162/// # Ok(())
1163/// # }
1164/// ```
1165pub struct MaintenanceOperations<'a> {
1166 pub(crate) framework: &'a AuthFramework,
1167}
1168
1169impl MaintenanceOperations<'_> {
1170 /// Export a logical snapshot to disk.
1171 ///
1172 /// Uses [`ExecutionMode`] for clarity at call sites.
1173 pub async fn backup(&self, output_path: &str, mode: ExecutionMode) -> Result<BackupReport> {
1174 crate::maintenance::backup_to_file(self.framework, output_path, mode.is_dry_run()).await
1175 }
1176
1177 /// Export a logical snapshot to disk (legacy API).
1178 ///
1179 /// Prefer [`backup`](Self::backup) with an [`ExecutionMode`] for
1180 /// self-documenting call sites.
1181 pub async fn backup_to_file(&self, output_path: &str, dry_run: bool) -> Result<BackupReport> {
1182 crate::maintenance::backup_to_file(self.framework, output_path, dry_run).await
1183 }
1184
1185 /// Restore a logical snapshot from disk.
1186 ///
1187 /// Uses [`ExecutionMode`] for clarity at call sites.
1188 pub async fn restore(&self, backup_path: &str, mode: ExecutionMode) -> Result<RestoreReport> {
1189 crate::maintenance::restore_from_file(self.framework, backup_path, mode.is_dry_run()).await
1190 }
1191
1192 /// Restore a logical snapshot from disk (legacy API).
1193 ///
1194 /// Prefer [`restore`](Self::restore) with an [`ExecutionMode`] for
1195 /// self-documenting call sites.
1196 pub async fn restore_from_file(
1197 &self,
1198 backup_path: &str,
1199 dry_run: bool,
1200 ) -> Result<RestoreReport> {
1201 crate::maintenance::restore_from_file(self.framework, backup_path, dry_run).await
1202 }
1203
1204 /// Reset logical authentication state in the configured backend.
1205 ///
1206 /// Uses [`ExecutionMode`] for clarity at call sites.
1207 pub async fn reset_with_mode(&self, mode: ExecutionMode) -> Result<ResetReport> {
1208 crate::maintenance::reset_runtime_data(self.framework, mode.is_dry_run()).await
1209 }
1210
1211 /// Reset logical authentication state in the configured backend (legacy API).
1212 ///
1213 /// Prefer [`reset_with_mode`](Self::reset_with_mode) with an
1214 /// [`ExecutionMode`] for self-documenting call sites.
1215 pub async fn reset(&self, dry_run: bool) -> Result<ResetReport> {
1216 crate::maintenance::reset_runtime_data(self.framework, dry_run).await
1217 }
1218}
1219
1220// ──────────────────────────────────────────────────────────────────────────────
1221// MFA operations
1222// ──────────────────────────────────────────────────────────────────────────────
1223
1224/// Focused multi-factor authentication operations exposed via [`AuthFramework::mfa()`].
1225///
1226/// Covers TOTP, SMS, email challenges, backup codes, and MFA completion
1227/// flows.
1228///
1229/// # Example
1230///
1231/// ```rust,no_run
1232/// # async fn example(auth: &auth_framework::AuthFramework) -> auth_framework::Result<()> {
1233/// let mfa = auth.mfa();
1234///
1235/// // Set up TOTP for a user
1236/// let secret = mfa.generate_totp_secret("user_123").await?;
1237/// let qr_url = mfa.generate_totp_qr_url("user_123", "MyApp", &secret).await?;
1238///
1239/// // Verify a code supplied by the user
1240/// let valid = mfa.verify_totp("user_123", "123456").await?;
1241/// # Ok(())
1242/// # }
1243/// ```
1244pub struct MfaOperations<'a> {
1245 pub(crate) framework: &'a AuthFramework,
1246}
1247
1248impl MfaOperations<'_> {
1249 /// Complete a pending MFA challenge with the provided code.
1250 pub async fn complete(&self, challenge: MfaChallenge, code: &str) -> Result<AuthToken> {
1251 self.framework.complete_mfa(challenge, code).await
1252 }
1253
1254 /// Complete a pending MFA challenge by its ID.
1255 pub async fn complete_by_id(&self, challenge_id: &str, code: &str) -> Result<AuthToken> {
1256 self.framework.complete_mfa_by_id(challenge_id, code).await
1257 }
1258
1259 /// Initiate an SMS-based MFA challenge for a user.
1260 pub async fn initiate_sms(&self, user_id: &str) -> Result<String> {
1261 self.framework.initiate_sms_challenge(user_id).await
1262 }
1263
1264 /// Verify an SMS challenge code.
1265 pub async fn verify_sms(&self, challenge_id: &str, code: &str) -> Result<bool> {
1266 self.framework.verify_sms_code(challenge_id, code).await
1267 }
1268
1269 /// Initiate an email-based MFA challenge for a user.
1270 pub async fn initiate_email(&self, user_id: &str) -> Result<String> {
1271 self.framework.initiate_email_challenge(user_id).await
1272 }
1273
1274 /// Register a phone number for SMS MFA.
1275 pub async fn register_phone(&self, user_id: &str, phone_number: &str) -> Result<()> {
1276 self.framework
1277 .register_phone_number(user_id, phone_number)
1278 .await
1279 }
1280
1281 /// Register an email address for email MFA.
1282 pub async fn register_email(&self, user_id: &str, email: &str) -> Result<()> {
1283 self.framework.register_email(user_id, email).await
1284 }
1285
1286 /// Generate a TOTP secret for a user.
1287 pub async fn generate_totp_secret(&self, user_id: &str) -> Result<String> {
1288 self.framework.generate_totp_secret(user_id).await
1289 }
1290
1291 /// Generate a TOTP QR code provisioning URL.
1292 pub async fn generate_totp_qr_url(
1293 &self,
1294 user_id: &str,
1295 app_name: &str,
1296 secret: &str,
1297 ) -> Result<String> {
1298 self.framework
1299 .generate_totp_qr_code(user_id, app_name, secret)
1300 .await
1301 }
1302
1303 /// Generate the current TOTP code for a secret.
1304 pub async fn generate_totp_code(&self, secret: &str) -> Result<String> {
1305 self.framework.generate_totp_code(secret).await
1306 }
1307
1308 /// Verify a TOTP code for a user.
1309 pub async fn verify_totp(&self, user_id: &str, code: &str) -> Result<bool> {
1310 self.framework.verify_totp_code(user_id, code).await
1311 }
1312
1313 /// Generate backup codes for a user.
1314 pub async fn generate_backup_codes(&self, user_id: &str, count: usize) -> Result<Vec<String>> {
1315 self.framework.generate_backup_codes(user_id, count).await
1316 }
1317}
1318
1319// ──────────────────────────────────────────────────────────────────────────────
1320// Monitoring operations
1321// ──────────────────────────────────────────────────────────────────────────────
1322
1323/// Focused monitoring and health operations exposed via [`AuthFramework::monitoring()`].
1324///
1325/// Health-checks, performance and security metrics, Prometheus export, and
1326/// rate-limit inspection.
1327///
1328/// # Example
1329///
1330/// ```rust,no_run
1331/// # async fn example(auth: &auth_framework::AuthFramework) -> auth_framework::Result<()> {
1332/// let mon = auth.monitoring();
1333///
1334/// let health = mon.health_check().await?;
1335/// let stats = mon.stats().await?;
1336/// let prom = mon.prometheus_metrics().await;
1337/// # Ok(())
1338/// # }
1339/// ```
1340pub struct MonitoringOperations<'a> {
1341 pub(crate) framework: &'a AuthFramework,
1342}
1343
1344impl MonitoringOperations<'_> {
1345 /// Perform a comprehensive health check across all subsystems.
1346 ///
1347 /// Returns a map of subsystem name → health result (e.g. `"storage"`,
1348 /// `"token_manager"`, `"rate_limiter"`).
1349 pub async fn health_check(
1350 &self,
1351 ) -> Result<std::collections::HashMap<String, crate::monitoring::HealthCheckResult>> {
1352 self.framework.health_check().await
1353 }
1354
1355 /// Get current performance metrics.
1356 ///
1357 /// Returns a map of metric name → counter value (e.g.
1358 /// `"auth_requests"`, `"auth_successes"`, `"auth_failures"`).
1359 pub async fn performance_metrics(&self) -> std::collections::HashMap<String, u64> {
1360 self.framework.get_performance_metrics().await
1361 }
1362
1363 /// Get aggregated security metrics.
1364 ///
1365 /// Returns a map of metric name → counter value (e.g.
1366 /// `"brute_force_blocked"`, `"rate_limited_requests"`).
1367 pub async fn security_metrics(&self) -> Result<std::collections::HashMap<String, u64>> {
1368 self.framework.get_security_metrics().await
1369 }
1370
1371 /// Export metrics in Prometheus text format.
1372 pub async fn prometheus_metrics(&self) -> String {
1373 self.framework.export_prometheus_metrics().await
1374 }
1375
1376 /// Get authentication statistics.
1377 pub async fn stats(&self) -> Result<AuthStats> {
1378 self.framework.get_stats().await
1379 }
1380
1381 /// Check whether an IP address is within the configured rate limit.
1382 pub async fn check_ip_rate_limit(&self, ip: &str) -> Result<bool> {
1383 self.framework.check_ip_rate_limit(ip).await
1384 }
1385
1386 /// Access the underlying monitoring manager for advanced usage.
1387 pub fn manager(&self) -> Arc<crate::monitoring::MonitoringManager> {
1388 self.framework.monitoring_manager()
1389 }
1390}
1391
1392// ──────────────────────────────────────────────────────────────────────────────
1393// Audit operations
1394// ──────────────────────────────────────────────────────────────────────────────
1395
1396/// Focused audit log operations exposed via [`AuthFramework::audit()`].
1397///
1398/// Query permission audit logs, view permission metrics, and retrieve
1399/// comprehensive security audit statistics.
1400///
1401/// # Example
1402///
1403/// ```rust,no_run
1404/// # async fn example(auth: &auth_framework::AuthFramework) -> auth_framework::Result<()> {
1405/// let audit = auth.audit();
1406///
1407/// // Retrieve recent permission log entries for a specific user
1408/// let logs = audit.permission_logs(Some("user_123"), None, None, Some(50)).await?;
1409///
1410/// // Get security audit statistics
1411/// let stats = audit.security_stats().await?;
1412/// # Ok(())
1413/// # }
1414/// ```
1415pub struct AuditOperations<'a> {
1416 pub(crate) framework: &'a AuthFramework,
1417}
1418
1419impl AuditOperations<'_> {
1420 /// Query permission-related audit log entries using an [`AuditLogQuery`].
1421 ///
1422 /// # Example
1423 ///
1424 /// ```rust,ignore
1425 /// let logs = auth.audit()
1426 /// .query_permission_logs(
1427 /// AuditLogQuery::new()
1428 /// .user("user_123")
1429 /// .action("read")
1430 /// .limit(50)
1431 /// )
1432 /// .await?;
1433 /// ```
1434 pub async fn query_permission_logs(&self, query: AuditLogQuery) -> Result<Vec<String>> {
1435 self.framework
1436 .get_permission_audit_logs(
1437 query.user_id.as_deref(),
1438 query.action.as_deref(),
1439 query.resource.as_deref(),
1440 query.limit,
1441 )
1442 .await
1443 }
1444
1445 /// Query permission-related audit log entries with optional filters.
1446 ///
1447 /// Prefer [`query_permission_logs`](Self::query_permission_logs) with an
1448 /// [`AuditLogQuery`] for better readability when using multiple filters.
1449 pub async fn permission_logs(
1450 &self,
1451 user_id: Option<&str>,
1452 action: Option<&str>,
1453 resource: Option<&str>,
1454 limit: Option<usize>,
1455 ) -> Result<Vec<String>> {
1456 self.framework
1457 .get_permission_audit_logs(user_id, action, resource, limit)
1458 .await
1459 }
1460
1461 /// Get aggregated permission metrics (role counts, active sessions, checks per hour).
1462 pub async fn permission_metrics(&self) -> Result<std::collections::HashMap<String, u64>> {
1463 self.framework.get_permission_metrics().await
1464 }
1465
1466 /// Get comprehensive security audit statistics.
1467 pub async fn security_stats(&self) -> Result<SecurityAuditStats> {
1468 self.framework.get_security_audit_stats().await
1469 }
1470}
1471#[cfg(test)]
1472mod tests {
1473 use super::*;
1474 use crate::config::AuthConfig;
1475
1476 async fn make_fw() -> AuthFramework {
1477 let config = AuthConfig::new().secret("test_ops_secret_key_32bytes_long!".to_string());
1478 let mut fw = AuthFramework::new(config);
1479 fw.initialize().await.unwrap();
1480 fw
1481 }
1482
1483 // ── UserOperations ──────────────────────────────────────────────────
1484
1485 #[tokio::test]
1486 async fn test_user_ops_register_and_get() {
1487 let fw = make_fw().await;
1488 let uid = fw
1489 .users()
1490 .register("ops_user1", "ops1@test.com", "StrongP@ss1!")
1491 .await
1492 .unwrap();
1493 let info = fw.users().get(&uid).await.unwrap();
1494 assert_eq!(info.username, "ops_user1");
1495 }
1496
1497 #[tokio::test]
1498 async fn test_user_ops_list_empty() {
1499 let fw = make_fw().await;
1500 let list = fw.users().list_with_query(UserListQuery::new().limit(10)).await.unwrap();
1501 assert!(list.is_empty());
1502 }
1503
1504 #[tokio::test]
1505 async fn test_user_ops_exists_by_username() {
1506 let fw = make_fw().await;
1507 fw.users()
1508 .register("exists_u", "exists@test.com", "StrongP@ss1!")
1509 .await
1510 .unwrap();
1511 assert!(fw.users().exists_by_username("exists_u").await.unwrap());
1512 assert!(!fw.users().exists_by_username("nope_u").await.unwrap());
1513 }
1514
1515 #[tokio::test]
1516 async fn test_user_ops_exists_by_email() {
1517 let fw = make_fw().await;
1518 fw.users()
1519 .register("email_u", "email_exists@test.com", "StrongP@ss1!")
1520 .await
1521 .unwrap();
1522 assert!(
1523 fw.users()
1524 .exists_by_email("email_exists@test.com")
1525 .await
1526 .unwrap()
1527 );
1528 assert!(!fw.users().exists_by_email("no@test.com").await.unwrap());
1529 }
1530
1531 #[tokio::test]
1532 async fn test_user_ops_profile() {
1533 let fw = make_fw().await;
1534 let uid = fw
1535 .users()
1536 .register("profile_u", "prof@test.com", "StrongP@ss1!")
1537 .await
1538 .unwrap();
1539 let profile = fw.users().profile(&uid).await.unwrap();
1540 assert_eq!(profile.display_name().unwrap_or_default(), "profile_u");
1541 }
1542
1543 #[tokio::test]
1544 async fn test_user_ops_update_password_and_verify() {
1545 let fw = make_fw().await;
1546 let uid = fw
1547 .users()
1548 .register("pw_user", "pw@test.com", "StrongP@ss1!")
1549 .await
1550 .unwrap();
1551 fw.users()
1552 .update_password("pw_user", "NewStr0ng!Pass")
1553 .await
1554 .unwrap();
1555 assert!(
1556 fw.users()
1557 .verify_password(&uid, "NewStr0ng!Pass")
1558 .await
1559 .unwrap()
1560 );
1561 }
1562
1563 #[tokio::test]
1564 async fn test_user_ops_update_email() {
1565 let fw = make_fw().await;
1566 let uid = fw
1567 .users()
1568 .register("email_upd_u", "old@test.com", "StrongP@ss1!")
1569 .await
1570 .unwrap();
1571 fw.users().update_email(&uid, "new@test.com").await.unwrap();
1572 assert!(fw.users().exists_by_email("new@test.com").await.unwrap());
1573 }
1574
1575 #[tokio::test]
1576 async fn test_user_ops_set_active() {
1577 let fw = make_fw().await;
1578 let uid = fw
1579 .users()
1580 .register("act_u", "act@test.com", "StrongP@ss1!")
1581 .await
1582 .unwrap();
1583 fw.users()
1584 .set_status(&uid, UserStatus::Inactive)
1585 .await
1586 .unwrap();
1587 let info = fw.users().get(&uid).await.unwrap();
1588 assert!(!info.active);
1589 }
1590
1591 #[tokio::test]
1592 async fn test_user_ops_delete() {
1593 let fw = make_fw().await;
1594 fw.users()
1595 .register("del_u", "del@test.com", "StrongP@ss1!")
1596 .await
1597 .unwrap();
1598 fw.users().delete("del_u").await.unwrap();
1599 assert!(!fw.users().exists_by_username("del_u").await.unwrap());
1600 }
1601
1602 #[tokio::test]
1603 async fn test_user_ops_username() {
1604 let fw = make_fw().await;
1605 let uid = fw
1606 .users()
1607 .register("name_u", "name@test.com", "StrongP@ss1!")
1608 .await
1609 .unwrap();
1610 let name = fw.users().username(&uid).await.unwrap();
1611 assert_eq!(name, "name_u");
1612 }
1613
1614 // ── SessionOperations ───────────────────────────────────────────────
1615
1616 #[tokio::test]
1617 async fn test_session_ops_create_get_delete() {
1618 let fw = make_fw().await;
1619 let uid = fw
1620 .users()
1621 .register("sess_u", "sess@test.com", "StrongP@ss1!")
1622 .await
1623 .unwrap();
1624 let sid = fw
1625 .sessions()
1626 .create(&uid, Duration::from_secs(3600), None, None)
1627 .await
1628 .unwrap();
1629 let sess = fw.sessions().get(&sid).await.unwrap();
1630 assert!(sess.is_some());
1631 fw.sessions().delete(&sid).await.unwrap();
1632 assert!(fw.sessions().get(&sid).await.unwrap().is_none());
1633 }
1634
1635 #[tokio::test]
1636 async fn test_session_ops_list_for_user() {
1637 let fw = make_fw().await;
1638 let uid = fw
1639 .users()
1640 .register("sl_u", "sl@test.com", "StrongP@ss1!")
1641 .await
1642 .unwrap();
1643 fw.sessions()
1644 .create(&uid, Duration::from_secs(3600), None, None)
1645 .await
1646 .unwrap();
1647 let list = fw.sessions().list_for_user(&uid).await.unwrap();
1648 assert_eq!(list.len(), 1);
1649 }
1650
1651 #[tokio::test]
1652 async fn test_session_ops_cleanup_expired() {
1653 let fw = make_fw().await;
1654 // Should not error even with no sessions
1655 fw.sessions().cleanup_expired().await.unwrap();
1656 }
1657
1658 // ── TokenOperations ─────────────────────────────────────────────────
1659
1660 #[tokio::test]
1661 async fn test_token_ops_create_validate_revoke() {
1662 let fw = make_fw().await;
1663 let uid = fw
1664 .users()
1665 .register("tok_u", "tok@test.com", "StrongP@ss1!")
1666 .await
1667 .unwrap();
1668 // Create API key instead of auth token—no auth method registration needed
1669 let key = fw.tokens().create_api_key(&uid, None).await.unwrap();
1670 let info = fw.tokens().validate_api_key(&key).await.unwrap();
1671 assert!(!info.id.is_empty());
1672 fw.tokens().revoke_api_key(&key).await.unwrap();
1673 }
1674
1675 #[tokio::test]
1676 async fn test_token_ops_api_key_lifecycle() {
1677 let fw = make_fw().await;
1678 let uid = fw
1679 .users()
1680 .register("apikey_u", "apikey@test.com", "StrongP@ss1!")
1681 .await
1682 .unwrap();
1683 let key = fw.tokens().create_api_key(&uid, None).await.unwrap();
1684 let info = fw.tokens().validate_api_key(&key).await.unwrap();
1685 // API key creates an internal user; just verify it resolves
1686 assert!(!info.id.is_empty());
1687 fw.tokens().revoke_api_key(&key).await.unwrap();
1688 assert!(fw.tokens().validate_api_key(&key).await.is_err());
1689 }
1690
1691 // ── AuthorizationOperations ─────────────────────────────────────────
1692
1693 #[tokio::test]
1694 async fn test_authz_ops_grant_and_check() {
1695 let fw = make_fw().await;
1696 let uid = fw
1697 .users()
1698 .register("authz_u", "authz@test.com", "StrongP@ss1!")
1699 .await
1700 .unwrap();
1701 fw.authorization()
1702 .grant(&uid, "read", "docs")
1703 .await
1704 .unwrap();
1705 // Verify via has_role / effective_permissions rather than via token (no auth method registered)
1706 let perms = fw
1707 .authorization()
1708 .effective_permissions(&uid)
1709 .await
1710 .unwrap();
1711 assert!(perms.iter().any(|p| p.contains("read")));
1712 }
1713
1714 #[tokio::test]
1715 async fn test_authz_ops_role_lifecycle() {
1716 let fw = make_fw().await;
1717 let uid = fw
1718 .users()
1719 .register("role_u", "role@test.com", "StrongP@ss1!")
1720 .await
1721 .unwrap();
1722 fw.authorization().assign_role(&uid, "admin").await.unwrap();
1723 assert!(fw.authorization().has_role(&uid, "admin").await.unwrap());
1724 let roles = fw.authorization().roles_for_user(&uid).await.unwrap();
1725 assert!(roles.contains(&"admin".to_string()));
1726 fw.authorization().remove_role(&uid, "admin").await.unwrap();
1727 assert!(!fw.authorization().has_role(&uid, "admin").await.unwrap());
1728 }
1729
1730 // ── MfaOperations ───────────────────────────────────────────────────
1731
1732 #[tokio::test]
1733 async fn test_mfa_ops_totp_lifecycle() {
1734 let fw = make_fw().await;
1735 let uid = fw
1736 .users()
1737 .register("mfa_u", "mfa@test.com", "StrongP@ss1!")
1738 .await
1739 .unwrap();
1740 let secret = fw.mfa().generate_totp_secret(&uid).await.unwrap();
1741 assert!(!secret.is_empty());
1742 let code = fw.mfa().generate_totp_code(&secret).await.unwrap();
1743 assert_eq!(code.len(), 6);
1744 assert!(fw.mfa().verify_totp(&uid, &code).await.unwrap());
1745 }
1746
1747 #[tokio::test]
1748 async fn test_mfa_ops_backup_codes() {
1749 let fw = make_fw().await;
1750 let uid = fw
1751 .users()
1752 .register("bc_u", "bc@test.com", "StrongP@ss1!")
1753 .await
1754 .unwrap();
1755 let codes = fw.mfa().generate_backup_codes(&uid, 5).await.unwrap();
1756 assert_eq!(codes.len(), 5);
1757 }
1758
1759 #[tokio::test]
1760 async fn test_mfa_ops_sms_lifecycle() {
1761 let fw = make_fw().await;
1762 let uid = fw
1763 .users()
1764 .register("smsop_u", "smsop@test.com", "StrongP@ss1!")
1765 .await
1766 .unwrap();
1767 fw.mfa().register_phone(&uid, "+12345678901").await.unwrap();
1768 let cid = fw.mfa().initiate_sms(&uid).await.unwrap();
1769 assert!(!cid.is_empty());
1770 }
1771
1772 #[tokio::test]
1773 async fn test_mfa_ops_email_lifecycle() {
1774 let fw = make_fw().await;
1775 let uid = fw
1776 .users()
1777 .register("emop_u", "emop@test.com", "StrongP@ss1!")
1778 .await
1779 .unwrap();
1780 fw.mfa()
1781 .register_email(&uid, "emop@test.com")
1782 .await
1783 .unwrap();
1784 let cid = fw.mfa().initiate_email(&uid).await.unwrap();
1785 assert!(!cid.is_empty());
1786 }
1787
1788 // ── MonitoringOperations ────────────────────────────────────────────
1789
1790 #[tokio::test]
1791 async fn test_monitoring_ops_health_check() {
1792 let fw = make_fw().await;
1793 let health = fw.monitoring().health_check().await.unwrap();
1794 assert!(!health.is_empty());
1795 }
1796
1797 #[tokio::test]
1798 async fn test_monitoring_ops_performance_metrics() {
1799 let fw = make_fw().await;
1800 let _metrics = fw.monitoring().performance_metrics().await;
1801 }
1802
1803 #[tokio::test]
1804 async fn test_monitoring_ops_stats() {
1805 let fw = make_fw().await;
1806 let stats = fw.monitoring().stats().await.unwrap();
1807 assert_eq!(stats.active_sessions, 0);
1808 }
1809
1810 #[tokio::test]
1811 async fn test_monitoring_ops_prometheus() {
1812 let fw = make_fw().await;
1813 let prom = fw.monitoring().prometheus_metrics().await;
1814 // Just verify it returns valid-looking metrics text
1815 assert!(!prom.is_empty());
1816 }
1817
1818 // ── AdminOperations ─────────────────────────────────────────────────
1819
1820 #[tokio::test]
1821 async fn test_admin_ops_role_inheritance() {
1822 let fw = make_fw().await;
1823 // Default roles include "admin" and "user" — use those
1824 fw.admin()
1825 .set_role_inheritance("user", "admin")
1826 .await
1827 .unwrap();
1828 }
1829
1830 #[tokio::test]
1831 async fn test_admin_ops_abac_policy() {
1832 let fw = make_fw().await;
1833 fw.admin()
1834 .create_abac_policy("ip_allow", "IP allowlist policy")
1835 .await
1836 .unwrap();
1837 }
1838
1839 #[tokio::test]
1840 async fn test_admin_ops_user_attributes() {
1841 let fw = make_fw().await;
1842 let uid = fw
1843 .users()
1844 .register("attr_u", "attr@test.com", "StrongP@ss1!")
1845 .await
1846 .unwrap();
1847 fw.admin()
1848 .map_user_attribute(&uid, "department", "engineering")
1849 .await
1850 .unwrap();
1851 let val = fw
1852 .admin()
1853 .get_user_attribute(&uid, "department")
1854 .await
1855 .unwrap();
1856 assert_eq!(val.as_deref(), Some("engineering"));
1857 }
1858
1859 #[tokio::test]
1860 async fn test_admin_ops_delegation() {
1861 let fw = make_fw().await;
1862 let uid1 = fw
1863 .users()
1864 .register("del_from", "delf@test.com", "StrongP@ss1!")
1865 .await
1866 .unwrap();
1867 let uid2 = fw
1868 .users()
1869 .register("del_to", "delt@test.com", "StrongP@ss1!")
1870 .await
1871 .unwrap();
1872 fw.authorization()
1873 .grant(&uid1, "write", "report")
1874 .await
1875 .unwrap();
1876 fw.admin()
1877 .delegate_permission(&uid1, &uid2, "write", "report", Duration::from_secs(3600))
1878 .await
1879 .unwrap();
1880 }
1881
1882 // ── MaintenanceOperations ───────────────────────────────────────────
1883
1884 #[tokio::test]
1885 async fn test_maintenance_ops_backup_dry_run() {
1886 let fw = make_fw().await;
1887 let report = fw
1888 .maintenance()
1889 .backup_to_file("test_backup.json", true)
1890 .await
1891 .unwrap();
1892 assert!(report.dry_run);
1893 }
1894
1895 #[tokio::test]
1896 async fn test_maintenance_ops_reset_dry_run() {
1897 let fw = make_fw().await;
1898 let report = fw.maintenance().reset(true).await.unwrap();
1899 assert!(report.dry_run);
1900 }
1901
1902 // ── AuditOperations ─────────────────────────────────────────────────
1903
1904 #[tokio::test]
1905 async fn test_audit_ops_permission_logs() {
1906 let fw = make_fw().await;
1907 let logs = fw
1908 .audit()
1909 .permission_logs(None, None, None, Some(10))
1910 .await
1911 .unwrap();
1912 assert!(logs.is_empty()); // no activity yet
1913 }
1914
1915 #[tokio::test]
1916 async fn test_audit_ops_permission_metrics() {
1917 let fw = make_fw().await;
1918 let metrics = fw.audit().permission_metrics().await.unwrap();
1919 assert!(!metrics.is_empty());
1920 }
1921
1922 #[tokio::test]
1923 async fn test_audit_ops_security_stats() {
1924 let fw = make_fw().await;
1925 let stats = fw.audit().security_stats().await.unwrap();
1926 assert_eq!(stats.failed_logins_24h, 0);
1927 }
1928}
1929// ──────────────────────────────────────────────────────────────────────────────
1930// Admin operations
1931// ──────────────────────────────────────────────────────────────────────────────
1932
1933/// Focused advanced administration operations exposed from [`AuthFramework::admin`].
1934///
1935/// These operations go beyond the everyday [`AuthorizationOperations`] surface and cover
1936/// ABAC policy management, permission delegation, role inheritance, resource registration,
1937/// and attribute-based access control.
1938pub struct AdminOperations<'a> {
1939 pub(crate) framework: &'a AuthFramework,
1940}
1941
1942impl AdminOperations<'_> {
1943 /// Define a parent–child role inheritance relationship.
1944 pub async fn set_role_inheritance(&self, child_role: &str, parent_role: &str) -> Result<()> {
1945 self.framework
1946 .set_role_inheritance(child_role, parent_role)
1947 .await
1948 }
1949
1950 /// Create an ABAC policy.
1951 pub async fn create_abac_policy(&self, name: &str, description: &str) -> Result<()> {
1952 self.framework.create_abac_policy(name, description).await
1953 }
1954
1955 /// Map a user attribute used in ABAC policy evaluation.
1956 pub async fn map_user_attribute(
1957 &self,
1958 user_id: &str,
1959 attribute: &str,
1960 value: &str,
1961 ) -> Result<()> {
1962 self.framework
1963 .map_user_attribute(user_id, attribute, value)
1964 .await
1965 }
1966
1967 /// Set multiple user attributes in one call.
1968 ///
1969 /// This is a convenience wrapper around [`map_user_attribute`](Self::map_user_attribute)
1970 /// for setting several ABAC attributes at once.
1971 ///
1972 /// # Example
1973 ///
1974 /// ```rust,no_run
1975 /// # use auth_framework::prelude::*;
1976 /// # async fn example(auth: &AuthFramework) -> Result<(), AuthError> {
1977 /// auth.admin().set_user_attributes("user-1", &[
1978 /// ("department", "engineering"),
1979 /// ("clearance", "top-secret"),
1980 /// ("location", "us-west-2"),
1981 /// ]).await?;
1982 /// # Ok(())
1983 /// # }
1984 /// ```
1985 pub async fn set_user_attributes(
1986 &self,
1987 user_id: &str,
1988 attributes: &[(&str, &str)],
1989 ) -> Result<()> {
1990 for &(attribute, value) in attributes {
1991 self.framework
1992 .map_user_attribute(user_id, attribute, value)
1993 .await?;
1994 }
1995 Ok(())
1996 }
1997
1998 /// Get a user attribute value.
1999 pub async fn get_user_attribute(
2000 &self,
2001 user_id: &str,
2002 attribute: &str,
2003 ) -> Result<Option<String>> {
2004 self.framework.get_user_attribute(user_id, attribute).await
2005 }
2006
2007 /// Check a permission using dynamic ABAC context evaluation.
2008 ///
2009 /// Prefer [`check_dynamic_permission_with_context`](Self::check_dynamic_permission_with_context)
2010 /// with a [`PermissionContext`] for a more readable API.
2011 pub async fn check_dynamic_permission(
2012 &self,
2013 user_id: &str,
2014 action: &str,
2015 resource: &str,
2016 context: std::collections::HashMap<String, String>,
2017 ) -> Result<bool> {
2018 self.framework
2019 .check_dynamic_permission(user_id, action, resource, context)
2020 .await
2021 }
2022
2023 /// Check a permission using dynamic ABAC context evaluation with a
2024 /// [`PermissionContext`].
2025 ///
2026 /// # Example
2027 ///
2028 /// ```rust,ignore
2029 /// use auth_framework::auth_operations::PermissionContext;
2030 ///
2031 /// let ctx = PermissionContext::new()
2032 /// .with_attribute("ip_location", "office")
2033 /// .with_attribute("device_type", "trusted");
2034 ///
2035 /// let allowed = auth.admin()
2036 /// .check_dynamic_permission_with_context("user_123", "read", "docs", ctx)
2037 /// .await?;
2038 /// ```
2039 pub async fn check_dynamic_permission_with_context(
2040 &self,
2041 user_id: &str,
2042 action: &str,
2043 resource: &str,
2044 context: PermissionContext,
2045 ) -> Result<bool> {
2046 self.framework
2047 .check_dynamic_permission(user_id, action, resource, context.into_attributes())
2048 .await
2049 }
2050
2051 /// Register a resource in the permission system.
2052 pub async fn create_resource(&self, resource: &str) -> Result<()> {
2053 self.framework.create_resource(resource).await
2054 }
2055
2056 /// Delegate a permission from one user to another using a [`DelegationRequest`].
2057 ///
2058 /// # Example
2059 ///
2060 /// ```rust,ignore
2061 /// auth.admin()
2062 /// .delegate(
2063 /// DelegationRequest::new("admin_1", "user_2", "write", "reports")
2064 /// .duration(Duration::from_secs(3600))
2065 /// )
2066 /// .await?;
2067 /// ```
2068 pub async fn delegate(&self, req: DelegationRequest) -> Result<()> {
2069 self.framework
2070 .delegate_permission(
2071 &req.delegator_id,
2072 &req.delegatee_id,
2073 &req.action,
2074 &req.resource,
2075 req.duration,
2076 )
2077 .await
2078 }
2079
2080 /// Delegate a permission from one user to another for a limited duration.
2081 ///
2082 /// Prefer [`delegate`](Self::delegate) with a [`DelegationRequest`] for
2083 /// better readability.
2084 pub async fn delegate_permission(
2085 &self,
2086 delegator_id: &str,
2087 delegatee_id: &str,
2088 action: &str,
2089 resource: &str,
2090 duration: Duration,
2091 ) -> Result<()> {
2092 self.framework
2093 .delegate_permission(delegator_id, delegatee_id, action, resource, duration)
2094 .await
2095 }
2096
2097 /// List currently active permission delegations for a user.
2098 pub async fn active_delegations(&self, user_id: &str) -> Result<Vec<String>> {
2099 self.framework.get_active_delegations(user_id).await
2100 }
2101}