1pub mod sessions;
27
28pub mod core;
30
31pub mod current_user;
33pub use current_user::CurrentUser;
34
35pub use core::{
37 AllowAny, AnonymousUser, AuthBackend, BaseUser, CompositeAuthBackend, FullUser, IsActiveUser,
38 IsAdminUser, IsAuthenticated, IsAuthenticatedOrReadOnly, PasswordHasher, Permission,
39 PermissionContext, PermissionsMixin, SimpleUser, User,
40};
41
42#[cfg(feature = "argon2-hasher")]
43pub use core::Argon2Hasher;
44
45pub use core::permission_operators;
47
48pub mod advanced_permissions;
50pub mod base_user_manager;
51pub mod basic;
52pub mod default_user;
53pub mod default_user_manager;
54pub mod group_management;
55#[cfg(feature = "sessions")]
56pub mod handlers;
57pub mod ip_permission;
58#[cfg(feature = "jwt")]
59pub mod jwt;
60pub mod mfa;
61pub mod model_permissions;
62#[cfg(feature = "oauth")]
63pub mod oauth2;
64pub mod object_permissions;
65#[cfg(feature = "rate-limit")]
66pub mod rate_limit_permission;
67pub mod remote_user;
68pub mod rest_authentication;
69#[cfg(feature = "sessions")]
70pub mod session;
71#[cfg(feature = "social")]
72pub mod social;
73pub mod time_based_permission;
74#[cfg(any(feature = "jwt", feature = "token"))]
75pub mod token_blacklist;
76#[cfg(any(feature = "jwt", feature = "token"))]
77pub mod token_rotation;
78#[cfg(any(feature = "jwt", feature = "token"))]
79pub mod token_storage;
80pub mod user_management;
81
82pub use advanced_permissions::{ObjectPermission as AdvancedObjectPermission, RoleBasedPermission};
83pub use base_user_manager::BaseUserManager;
84pub use basic::BasicAuthentication as HttpBasicAuth;
85#[cfg(feature = "argon2-hasher")]
86pub use default_user::DefaultUser;
87#[cfg(feature = "argon2-hasher")]
88pub use default_user_manager::DefaultUserManager;
89pub use group_management::{
90 CreateGroupData, Group, GroupManagementError, GroupManagementResult, GroupManager,
91};
92#[cfg(feature = "sessions")]
93pub use handlers::{LoginCredentials, LoginHandler, LogoutHandler, SESSION_COOKIE_NAME};
94pub use ip_permission::{CidrRange, IpBlacklistPermission, IpWhitelistPermission};
95#[cfg(feature = "jwt")]
96pub use jwt::{Claims, JwtAuth};
97pub use mfa::MFAAuthentication as MfaManager;
98pub use model_permissions::{
99 DjangoModelPermissions, DjangoModelPermissionsOrAnonReadOnly, ModelPermission,
100};
101#[cfg(feature = "oauth")]
102pub use oauth2::{
103 AccessToken, AuthorizationCode, GrantType, InMemoryOAuth2Store, OAuth2Application,
104 OAuth2Authentication, OAuth2TokenStore, SimpleUserRepository, UserRepository,
105};
106pub use object_permissions::{ObjectPermission, ObjectPermissionChecker, ObjectPermissionManager};
107pub use permission_operators::{AndPermission, NotPermission, OrPermission};
108#[cfg(feature = "social")]
109pub use social::{
110 AppleProvider, GitHubProvider, GoogleProvider, IdToken, MicrosoftProvider, OAuthProvider,
111 OAuthToken, PkceFlow, ProviderConfig, SocialAuthBackend, SocialAuthError, StandardClaims,
112 StateStore, TokenResponse,
113};
114
115#[cfg(feature = "rate-limit")]
116pub use rate_limit_permission::{RateLimitPermission, RateLimitPermissionBuilder};
117pub use remote_user::RemoteUserAuthentication as RemoteUserAuth;
118pub use rest_authentication::{
119 BasicAuthConfig, CompositeAuthentication, RemoteUserAuthentication, RestAuthentication,
120 SessionAuthConfig, SessionAuthentication, TokenAuthConfig, TokenAuthentication,
121};
122#[cfg(feature = "sessions")]
123pub use session::{InMemorySessionStore, SESSION_KEY_USER_ID, Session, SessionId, SessionStore};
124pub use time_based_permission::{DateRange, TimeBasedPermission, TimeWindow};
125#[cfg(any(feature = "jwt", feature = "token"))]
126pub use token_blacklist::{
127 BlacklistReason, BlacklistStats, BlacklistedToken, InMemoryRefreshTokenStore,
128 InMemoryTokenBlacklist, RefreshToken, RefreshTokenStore, TokenBlacklist, TokenRotationManager,
129};
130#[cfg(any(feature = "jwt", feature = "token"))]
131pub use token_rotation::{AutoTokenRotationManager, TokenRotationConfig, TokenRotationRecord};
132#[cfg(feature = "database")]
133pub use token_storage::DatabaseTokenStorage;
134#[cfg(any(feature = "jwt", feature = "token"))]
135pub use token_storage::{
136 InMemoryTokenStorage, StoredToken, TokenStorage, TokenStorageError, TokenStorageResult,
137};
138pub use user_management::{
139 CreateUserData, UpdateUserData, UserManagementError, UserManagementResult, UserManager,
140};
141
142#[derive(Debug, Clone)]
144pub enum AuthenticationError {
145 InvalidCredentials,
146 UserNotFound,
147 SessionExpired,
148 InvalidToken,
149 NotAuthenticated,
150 DatabaseError(String),
151 Unknown(String),
152}
153
154impl std::fmt::Display for AuthenticationError {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self {
157 AuthenticationError::InvalidCredentials => write!(f, "Invalid credentials"),
158 AuthenticationError::UserNotFound => write!(f, "User not found"),
159 AuthenticationError::SessionExpired => write!(f, "Session expired"),
160 AuthenticationError::InvalidToken => write!(f, "Invalid token"),
161 AuthenticationError::NotAuthenticated => write!(f, "User is not authenticated"),
162 AuthenticationError::DatabaseError(msg) => write!(f, "Database error: {}", msg),
163 AuthenticationError::Unknown(msg) => write!(f, "Authentication error: {}", msg),
164 }
165 }
166}
167
168impl std::error::Error for AuthenticationError {}
169
170#[async_trait::async_trait]
175pub trait AuthenticationBackend: Send + Sync {
176 async fn authenticate(
188 &self,
189 request: &reinhardt_http::Request,
190 ) -> Result<Option<Box<dyn User>>, AuthenticationError>;
191
192 async fn get_user(&self, user_id: &str) -> Result<Option<Box<dyn User>>, AuthenticationError>;
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use uuid::Uuid;
210
211 #[test]
212 #[cfg(feature = "jwt")]
213 fn test_auth_jwt_generate_unit() {
214 let jwt_auth = JwtAuth::new(b"test_secret_key");
215 let user_id = "user123".to_string();
216 let username = "testuser".to_string();
217
218 let token = jwt_auth.generate_token(user_id, username).unwrap();
219
220 assert!(!token.is_empty());
221 }
222
223 #[tokio::test]
224 async fn test_permission_allow_any() {
225 use bytes::Bytes;
226 use hyper::Method;
227 use reinhardt_http::Request;
228
229 let permission = AllowAny;
230 let request = Request::builder()
231 .method(Method::GET)
232 .uri("/test")
233 .body(Bytes::new())
234 .build()
235 .unwrap();
236
237 let context = PermissionContext {
238 request: &request,
239 is_authenticated: false,
240 is_admin: false,
241 is_active: false,
242 user: None,
243 };
244
245 assert!(permission.has_permission(&context).await);
246 }
247
248 #[tokio::test]
249 async fn test_permission_is_authenticated_with_auth() {
250 use bytes::Bytes;
251 use hyper::Method;
252 use reinhardt_http::Request;
253
254 let permission = IsAuthenticated;
255 let request = Request::builder()
256 .method(Method::GET)
257 .uri("/test")
258 .body(Bytes::new())
259 .build()
260 .unwrap();
261
262 let context = PermissionContext {
263 request: &request,
264 is_authenticated: true,
265 is_admin: false,
266 is_active: true,
267 user: None,
268 };
269
270 assert!(permission.has_permission(&context).await);
271 }
272
273 #[tokio::test]
274 async fn test_permission_is_authenticated_without_auth() {
275 use bytes::Bytes;
276 use hyper::Method;
277 use reinhardt_http::Request;
278
279 let permission = IsAuthenticated;
280 let request = Request::builder()
281 .method(Method::GET)
282 .uri("/test")
283 .body(Bytes::new())
284 .build()
285 .unwrap();
286
287 let context = PermissionContext {
288 request: &request,
289 is_authenticated: false,
290 is_admin: false,
291 is_active: false,
292 user: None,
293 };
294
295 assert!(!permission.has_permission(&context).await);
296 }
297
298 #[tokio::test]
299 async fn test_permission_is_admin_user() {
300 use bytes::Bytes;
301 use hyper::Method;
302 use reinhardt_http::Request;
303
304 let permission = IsAdminUser;
305 let request = Request::builder()
306 .method(Method::GET)
307 .uri("/test")
308 .body(Bytes::new())
309 .build()
310 .unwrap();
311
312 let context = PermissionContext {
314 request: &request,
315 is_authenticated: true,
316 is_admin: true,
317 is_active: true,
318 user: None,
319 };
320 assert!(permission.has_permission(&context).await);
321
322 let context = PermissionContext {
324 request: &request,
325 is_authenticated: true,
326 is_admin: false,
327 is_active: true,
328 user: None,
329 };
330 assert!(!permission.has_permission(&context).await);
331 }
332
333 #[tokio::test]
334 async fn test_permission_is_active_user() {
335 use bytes::Bytes;
336 use hyper::Method;
337 use reinhardt_http::Request;
338
339 let permission = IsActiveUser;
340 let request = Request::builder()
341 .method(Method::GET)
342 .uri("/test")
343 .body(Bytes::new())
344 .build()
345 .unwrap();
346
347 let context = PermissionContext {
349 request: &request,
350 is_authenticated: true,
351 is_admin: false,
352 is_active: true,
353 user: None,
354 };
355 assert!(permission.has_permission(&context).await);
356
357 let context = PermissionContext {
359 request: &request,
360 is_authenticated: true,
361 is_admin: false,
362 is_active: false,
363 user: None,
364 };
365 assert!(!permission.has_permission(&context).await);
366 }
367
368 #[tokio::test]
369 async fn test_permission_is_authenticated_or_read_only_get() {
370 use bytes::Bytes;
371 use hyper::Method;
372 use reinhardt_http::Request;
373
374 let permission = IsAuthenticatedOrReadOnly;
375 let request = Request::builder()
376 .method(Method::GET)
377 .uri("/test")
378 .body(Bytes::new())
379 .build()
380 .unwrap();
381
382 let context = PermissionContext {
384 request: &request,
385 is_authenticated: false,
386 is_admin: false,
387 is_active: false,
388 user: None,
389 };
390 assert!(permission.has_permission(&context).await);
391 }
392
393 #[tokio::test]
394 async fn test_permission_is_authenticated_or_read_only_post() {
395 use bytes::Bytes;
396 use hyper::Method;
397 use reinhardt_http::Request;
398
399 let permission = IsAuthenticatedOrReadOnly;
400 let request = Request::builder()
401 .method(Method::POST)
402 .uri("/test")
403 .body(Bytes::new())
404 .build()
405 .unwrap();
406
407 let context = PermissionContext {
409 request: &request,
410 is_authenticated: false,
411 is_admin: false,
412 is_active: false,
413 user: None,
414 };
415 assert!(!permission.has_permission(&context).await);
416
417 let context = PermissionContext {
419 request: &request,
420 is_authenticated: true,
421 is_admin: false,
422 is_active: true,
423 user: None,
424 };
425 assert!(permission.has_permission(&context).await);
426 }
427
428 #[test]
429 fn test_simple_user_implementation() {
430 let user = SimpleUser {
431 id: Uuid::new_v4(),
432 username: "testuser".to_string(),
433 email: "test@example.com".to_string(),
434 is_active: true,
435 is_admin: false,
436 is_staff: false,
437 is_superuser: false,
438 };
439
440 assert!(!user.id().is_empty());
441 assert_eq!(user.username(), "testuser");
442 assert!(user.is_authenticated());
443 assert!(user.is_active());
444 assert!(!user.is_admin());
445 }
446
447 #[test]
448 fn test_anonymous_user() {
449 let user = AnonymousUser;
450
451 assert_eq!(user.id(), "");
452 assert_eq!(user.username(), "");
453 assert!(!user.is_authenticated());
454 assert!(!user.is_active());
455 assert!(!user.is_admin());
456 }
457}