1use crate::api::{ApiResponse, ApiState};
7use crate::tokens::AuthToken;
8use axum::{
9 Extension,
10 extract::{Path, Query, State},
11 http::StatusCode,
12 response::Json,
13};
14use role_system::Permission;
15use serde::{Deserialize, Serialize};
16use std::collections::HashMap;
17use tracing::debug;
18use tracing::{info, warn};
19
20#[derive(Debug, Deserialize)]
22pub struct CreateRoleRequest {
23 pub name: String,
24 pub description: Option<String>,
25 pub parent_id: Option<String>,
26 pub permissions: Option<Vec<String>>,
27}
28
29#[derive(Debug, Deserialize)]
31pub struct UpdateRoleRequest {
32 pub name: Option<String>,
33 pub description: Option<String>,
34 pub parent_id: Option<String>,
35}
36
37#[derive(Debug, Deserialize)]
39pub struct CreatePermissionRequest {
40 pub action: String,
41 pub resource: String,
42 pub conditions: Option<HashMap<String, String>>,
43}
44
45#[derive(Debug, Deserialize)]
47pub struct AssignRoleRequest {
48 pub role_id: String,
49 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
50 pub reason: Option<String>,
51}
52
53#[derive(Debug, Deserialize)]
55pub struct BulkAssignRequest {
56 pub assignments: Vec<BulkAssignment>,
57}
58
59#[derive(Debug, Deserialize)]
60pub struct BulkAssignment {
61 pub user_id: String,
62 pub role_id: String,
63 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
64}
65
66#[derive(Debug, Deserialize)]
68pub struct ElevateRoleRequest {
69 pub target_role: String,
70 pub duration_minutes: Option<u32>,
71 pub justification: String,
72}
73
74#[derive(Debug, Serialize)]
76pub struct RoleResponse {
77 pub id: String,
78 pub name: String,
79 pub description: Option<String>,
80 pub parent_id: Option<String>,
81 pub permissions: Vec<String>,
82 pub created_at: chrono::DateTime<chrono::Utc>,
83 pub updated_at: chrono::DateTime<chrono::Utc>,
84}
85
86#[derive(Debug, Serialize)]
88pub struct PermissionResponse {
89 pub id: String,
90 pub action: String,
91 pub resource: String,
92 pub conditions: Option<HashMap<String, String>>,
93 pub created_at: chrono::DateTime<chrono::Utc>,
94}
95
96#[derive(Debug, Serialize)]
98pub struct UserRolesResponse {
99 pub user_id: String,
100 pub roles: Vec<UserRole>,
101 pub effective_permissions: Vec<String>,
102}
103
104#[derive(Debug, Serialize)]
105pub struct UserRole {
106 pub role_id: String,
107 pub role_name: String,
108 pub assigned_at: chrono::DateTime<chrono::Utc>,
109 pub assigned_by: Option<String>,
110 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
111}
112
113#[derive(Debug, Serialize)]
115pub struct AuditLogResponse {
116 pub entries: Vec<AuditEntryResponse>,
117 pub total_count: u64,
118 pub page: u32,
119 pub per_page: u32,
120}
121
122#[derive(Debug, Serialize)]
123pub struct AuditEntryResponse {
124 pub id: String,
125 pub user_id: Option<String>,
126 pub action: String,
127 pub resource: Option<String>,
128 pub result: String,
129 pub context: HashMap<String, String>,
130 pub timestamp: chrono::DateTime<chrono::Utc>,
131 pub description: String,
132}
133
134#[derive(Debug, Deserialize)]
136pub struct RoleListQuery {
137 pub page: Option<u32>,
138 pub per_page: Option<u32>,
139 pub parent_id: Option<String>,
140 pub include_permissions: Option<bool>,
141}
142
143#[derive(Debug, Deserialize)]
145pub struct AuditQuery {
146 pub user_id: Option<String>,
147 pub action: Option<String>,
148 pub resource: Option<String>,
149 pub start_time: Option<chrono::DateTime<chrono::Utc>>,
150 pub end_time: Option<chrono::DateTime<chrono::Utc>>,
151 pub page: Option<u32>,
152 pub per_page: Option<u32>,
153}
154
155#[derive(Debug, Deserialize)]
157pub struct PermissionCheckRequest {
158 pub action: String,
159 pub resource: String,
160 pub context: Option<HashMap<String, String>>,
161}
162
163#[derive(Debug, Serialize)]
165pub struct PermissionCheckResponse {
166 pub granted: bool,
167 pub reason: String,
168 pub required_roles: Vec<String>,
169 pub missing_permissions: Vec<String>,
170}
171
172pub async fn create_role(
179 State(state): State<ApiState>,
180 Extension(auth_token): Extension<AuthToken>,
181 Json(request): Json<CreateRoleRequest>,
182) -> Result<Json<ApiResponse<RoleResponse>>, StatusCode> {
183 if !auth_token.has_permission("manage:roles") {
185 return Ok(Json(
186 ApiResponse::<RoleResponse>::forbidden_with_message_typed(
187 "Insufficient permissions to manage roles",
188 ),
189 ));
190 }
191
192 let now = chrono::Utc::now();
193
194 let permissions: Vec<Permission> = request
196 .permissions
197 .unwrap_or_default()
198 .into_iter()
199 .filter_map(|perm_str| {
200 if let Some((action, resource)) = perm_str.split_once(':') {
202 Some(Permission::new(action, resource))
203 } else {
204 warn!("Invalid permission format: {}", perm_str);
205 None
206 }
207 })
208 .collect();
209
210 match state
211 .authorization_service
212 .create_role(
213 &request.name,
214 &request.description.unwrap_or_default(),
215 permissions,
216 request.parent_id.map(|p| vec![p]),
217 )
218 .await
219 {
220 Ok(_) => {
221 info!("Role created: {} by {}", request.name, auth_token.user_id);
222
223 match state.authorization_service.get_role(&request.name).await {
225 Ok(Some(role)) => {
226 let permissions_strings: Vec<String> = role
228 .permissions()
229 .permissions()
230 .iter()
231 .map(|p| format!("{}:{}", p.action(), p.resource_type()))
232 .collect();
233
234 let hierarchy_depth = role.hierarchy_depth();
236 let is_root = role.is_root_role();
237 let is_leaf = role.is_leaf_role();
238 let child_ids = role.child_role_ids();
239
240 debug!(
241 "Role '{}' - Depth: {}, Root: {}, Leaf: {}, Children: {:?}",
242 role.name(),
243 hierarchy_depth,
244 is_root,
245 is_leaf,
246 child_ids
247 );
248
249 let response = RoleResponse {
250 id: role.id().to_string(),
251 name: role.name().to_string(),
252 description: role.description().map(|s| s.to_string()),
253 parent_id: role.parent_role_id().map(|s| s.to_string()), permissions: permissions_strings,
255 created_at: now,
256 updated_at: now,
257 };
258
259 Ok(Json(ApiResponse::success(response)))
260 }
261 _ => Ok(Json(ApiResponse::<RoleResponse>::error_with_message_typed(
262 "ROLE_FETCH_FAILED",
263 "Role created but failed to fetch details",
264 ))),
265 }
266 }
267 Err(e) => {
268 warn!("Failed to create role: {}", e);
269 Ok(Json(ApiResponse::<RoleResponse>::error_with_message_typed(
270 "ROLE_CREATION_FAILED",
271 "Failed to create role",
272 )))
273 }
274 }
275}
276
277pub async fn get_role(
280 State(state): State<ApiState>,
281 Extension(auth_token): Extension<AuthToken>,
282 Path(role_id): Path<String>,
283) -> Result<Json<ApiResponse<RoleResponse>>, StatusCode> {
284 if !auth_token.has_permission("read:roles") {
286 return Ok(Json(
287 ApiResponse::<RoleResponse>::forbidden_with_message_typed(
288 "Insufficient permissions to read roles",
289 ),
290 ));
291 }
292
293 match state.authorization_service.get_role(&role_id).await {
294 Ok(Some(role)) => {
295 let permissions_strings: Vec<String> = role
296 .permissions()
297 .permissions()
298 .iter()
299 .map(|p| format!("{}:{}", p.action(), p.resource_type()))
300 .collect();
301
302 let response = RoleResponse {
303 id: role.id().to_string(),
304 name: role.name().to_string(),
305 description: role.description().map(|s| s.to_string()),
306 parent_id: role.parent_role_id().map(|s| s.to_string()), permissions: permissions_strings,
308 created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(),
310 };
311
312 Ok(Json(ApiResponse::success(response)))
313 }
314 Ok(None) => Ok(Json(
315 ApiResponse::<RoleResponse>::not_found_with_message_typed("Role not found"),
316 )),
317 Err(e) => {
318 warn!("Failed to get role: {}", e);
319 Ok(Json(ApiResponse::<RoleResponse>::error_with_message_typed(
320 "ROLE_FETCH_FAILED",
321 "Failed to fetch role",
322 )))
323 }
324 }
325}
326
327pub async fn list_roles(
330 State(state): State<ApiState>,
331 Extension(auth_token): Extension<AuthToken>,
332 Query(_query): Query<RoleListQuery>,
333) -> Result<Json<ApiResponse<Vec<RoleResponse>>>, StatusCode> {
334 if !auth_token.has_permission("read:roles") {
336 return Ok(Json(
337 ApiResponse::<Vec<RoleResponse>>::forbidden_with_message_typed(
338 "Insufficient permissions to read roles",
339 ),
340 ));
341 }
342
343 match state.authorization_service.list_roles().await {
344 Ok(roles) => {
345 let now = chrono::Utc::now();
346 let response: Vec<RoleResponse> = roles
347 .into_iter()
348 .map(|role| {
349 let permissions_strings: Vec<String> = role
350 .permissions()
351 .permissions()
352 .iter()
353 .map(|p| format!("{}:{}", p.action(), p.resource_type()))
354 .collect();
355 RoleResponse {
356 id: role.id().to_string(),
357 name: role.name().to_string(),
358 description: role.description().map(|s| s.to_string()),
359 parent_id: role.parent_role_id().map(|s| s.to_string()),
360 permissions: permissions_strings,
361 created_at: now,
362 updated_at: now,
363 }
364 })
365 .collect();
366 Ok(Json(ApiResponse::success(response)))
367 }
368 Err(e) => {
369 warn!("Failed to list roles: {}", e);
370 Ok(Json(
371 ApiResponse::<Vec<RoleResponse>>::error_with_message_typed(
372 "ROLE_LIST_FAILED",
373 "Failed to retrieve role list",
374 ),
375 ))
376 }
377 }
378}
379
380pub async fn update_role(
383 State(state): State<ApiState>,
384 Extension(auth_token): Extension<AuthToken>,
385 Path(role_id): Path<String>,
386 Json(request): Json<UpdateRoleRequest>,
387) -> Result<Json<ApiResponse<RoleResponse>>, StatusCode> {
388 if !auth_token.has_permission("manage:roles") {
390 return Ok(Json(
391 ApiResponse::<RoleResponse>::forbidden_with_message_typed(
392 "Insufficient permissions to manage roles",
393 ),
394 ));
395 }
396
397 let new_parent: Option<Option<&str>> = request.parent_id.as_deref().map(Some);
403
404 match state
405 .authorization_service
406 .update_role(&role_id, request.description.as_deref(), new_parent)
407 .await
408 {
409 Ok(()) => {
410 info!("Role {} updated by {}", role_id, auth_token.user_id);
411 match state.authorization_service.get_role(&role_id).await {
413 Ok(Some(role)) => {
414 let now = chrono::Utc::now();
415 let permissions_strings: Vec<String> = role
416 .permissions()
417 .permissions()
418 .iter()
419 .map(|p| format!("{}:{}", p.action(), p.resource_type()))
420 .collect();
421 Ok(Json(ApiResponse::success(RoleResponse {
422 id: role.id().to_string(),
423 name: role.name().to_string(),
424 description: role.description().map(|s| s.to_string()),
425 parent_id: role.parent_role_id().map(|s| s.to_string()),
426 permissions: permissions_strings,
427 created_at: now,
428 updated_at: now,
429 })))
430 }
431 _ => Ok(Json(ApiResponse::<RoleResponse>::error_with_message_typed(
432 "ROLE_FETCH_FAILED",
433 "Role updated but failed to fetch details",
434 ))),
435 }
436 }
437 Err(e) => {
438 warn!("Failed to update role {}: {}", role_id, e);
439 Ok(Json(ApiResponse::<RoleResponse>::error_with_message_typed(
440 "ROLE_UPDATE_FAILED",
441 "Failed to update role",
442 )))
443 }
444 }
445}
446
447pub async fn delete_role(
450 State(state): State<ApiState>,
451 Extension(auth_token): Extension<AuthToken>,
452 Path(role_id): Path<String>,
453) -> Result<Json<ApiResponse<()>>, StatusCode> {
454 if !auth_token.has_permission("manage:roles") {
456 return Ok(Json(ApiResponse::forbidden_typed()));
457 }
458
459 match state.authorization_service.delete_role(&role_id).await {
460 Ok(_) => {
461 info!("Role deleted: {} by {}", role_id, auth_token.user_id);
462 Ok(Json(ApiResponse::success(())))
463 }
464 Err(e) => {
465 warn!("Failed to delete role {}: {}", role_id, e);
466 Ok(Json(ApiResponse::<()>::error_typed(
467 "ROLE_DELETE_FAILED",
468 "Failed to delete role",
469 )))
470 }
471 }
472}
473
474pub async fn assign_user_role(
481 State(state): State<ApiState>,
482 Extension(auth_token): Extension<AuthToken>,
483 Path(user_id): Path<String>,
484 Json(request): Json<AssignRoleRequest>,
485) -> Result<Json<ApiResponse<()>>, StatusCode> {
486 if !auth_token.has_permission("manage:user_roles") {
488 return Ok(Json(ApiResponse::<()>::forbidden_with_message_typed(
489 "Insufficient permissions to manage user roles",
490 )));
491 }
492
493 match state
494 .authorization_service
495 .assign_role(&user_id, &request.role_id)
496 .await
497 {
498 Ok(_) => {
499 info!(
500 "Role {} assigned to user {} by {}",
501 request.role_id, user_id, auth_token.user_id
502 );
503 Ok(Json(ApiResponse::success(())))
504 }
505 Err(e) => {
506 warn!("Failed to assign role: {}", e);
507 Ok(Json(ApiResponse::<()>::error_with_message_typed(
508 "ROLE_ASSIGNMENT_FAILED",
509 "Failed to assign role",
510 )))
511 }
512 }
513}
514
515pub async fn revoke_user_role(
518 State(state): State<ApiState>,
519 Extension(auth_token): Extension<AuthToken>,
520 Path((user_id, role_id)): Path<(String, String)>,
521) -> Result<Json<ApiResponse<()>>, StatusCode> {
522 if !auth_token.has_permission("manage:user_roles") {
524 return Ok(Json(ApiResponse::<()>::forbidden_with_message_typed(
525 "Insufficient permissions to manage user roles",
526 )));
527 }
528
529 match state
530 .authorization_service
531 .remove_role(&user_id, &role_id)
532 .await
533 {
534 Ok(_) => {
535 info!(
536 "Role {} revoked from user {} by {}",
537 role_id, user_id, auth_token.user_id
538 );
539 Ok(Json(ApiResponse::success(())))
540 }
541 Err(e) => {
542 warn!("Failed to revoke role: {}", e);
543 Ok(Json(ApiResponse::<()>::error_with_message_typed(
544 "ROLE_REVOCATION_FAILED",
545 "Failed to revoke role",
546 )))
547 }
548 }
549}
550
551pub async fn get_user_roles(
554 State(state): State<ApiState>,
555 Extension(auth_token): Extension<AuthToken>,
556 Path(user_id): Path<String>,
557) -> Result<Json<ApiResponse<UserRolesResponse>>, StatusCode> {
558 if user_id != auth_token.user_id && !auth_token.has_permission("read:user_roles") {
560 return Ok(Json(
561 ApiResponse::<UserRolesResponse>::forbidden_with_message_typed(
562 "Insufficient permissions to read user roles",
563 ),
564 ));
565 }
566
567 match state.authorization_service.get_user_roles(&user_id).await {
568 Ok(role_names) => {
569 let roles: Vec<UserRole> = role_names
570 .into_iter()
571 .map(|name| UserRole {
572 role_id: name.clone(),
573 role_name: name,
574 assigned_at: chrono::DateTime::from_timestamp(0, 0)
577 .unwrap_or_else(chrono::Utc::now),
578 assigned_by: None,
579 expires_at: None,
580 })
581 .collect();
582
583 let effective_permissions: Vec<String> = if user_id == auth_token.user_id {
585 auth_token.permissions.to_vec()
586 } else {
587 vec![]
588 };
589
590 let response = UserRolesResponse {
591 user_id,
592 roles,
593 effective_permissions,
594 };
595
596 Ok(Json(ApiResponse::success(response)))
597 }
598 Err(e) => {
599 warn!("Failed to get roles for user {}: {}", user_id, e);
600 Ok(Json(
601 ApiResponse::<UserRolesResponse>::error_with_message_typed(
602 "ROLE_FETCH_FAILED",
603 "Failed to retrieve user roles",
604 ),
605 ))
606 }
607 }
608}
609
610pub async fn bulk_assign_roles(
613 State(state): State<ApiState>,
614 Extension(auth_token): Extension<AuthToken>,
615 Json(request): Json<BulkAssignRequest>,
616) -> Result<Json<ApiResponse<()>>, StatusCode> {
617 if !auth_token.has_permission("manage:user_roles") {
619 return Ok(Json(ApiResponse::<()>::forbidden_with_message_typed(
620 "Insufficient permissions to manage user roles",
621 )));
622 }
623
624 let mut success_count = 0;
626 let mut error_count = 0;
627
628 for assignment in request.assignments {
629 match state
630 .authorization_service
631 .assign_role(&assignment.user_id, &assignment.role_id)
632 .await
633 {
634 Ok(_) => success_count += 1,
635 Err(e) => {
636 warn!(
637 "Failed to assign role {} to user {}: {}",
638 assignment.role_id, assignment.user_id, e
639 );
640 error_count += 1;
641 }
642 }
643 }
644
645 info!(
646 "Bulk role assignment completed by {} - {} successes, {} errors",
647 auth_token.user_id, success_count, error_count
648 );
649
650 if error_count == 0 {
651 Ok(Json(ApiResponse::success(())))
652 } else {
653 Ok(Json(ApiResponse::<()>::error_with_message_typed(
654 "PARTIAL_BULK_ASSIGNMENT_FAILED",
655 format!(
656 "Bulk assignment partially failed: {} successes, {} errors",
657 success_count, error_count
658 ),
659 )))
660 }
661}
662
663pub async fn check_permission(
670 State(state): State<ApiState>,
671 Extension(auth_token): Extension<AuthToken>,
672 Json(request): Json<PermissionCheckRequest>,
673) -> Result<Json<ApiResponse<PermissionCheckResponse>>, StatusCode> {
674 let context = request.context.unwrap_or_default();
675
676 match state
677 .authorization_service
678 .check_permission(
679 &auth_token.user_id,
680 &request.action,
681 &request.resource,
682 Some(&context),
683 )
684 .await
685 {
686 Ok(granted) => {
687 let response = PermissionCheckResponse {
688 granted,
689 reason: if granted {
690 "Permission granted".to_string()
691 } else {
692 "Permission denied".to_string()
693 },
694 required_roles: vec![request.resource.clone()],
695 missing_permissions: if granted {
696 Vec::new()
697 } else {
698 vec![format!("{}:{}", request.action, request.resource)]
699 },
700 };
701
702 Ok(Json(ApiResponse::success(response)))
703 }
704 Err(e) => {
705 warn!("Permission check failed: {}", e);
706 Ok(Json(ApiResponse::<PermissionCheckResponse>::error_typed(
707 "PERMISSION_CHECK_FAILED",
708 "Failed to check permission",
709 )))
710 }
711 }
712}
713
714pub async fn elevate_role(
717 State(state): State<ApiState>,
718 Extension(auth_token): Extension<AuthToken>,
719 Json(request): Json<ElevateRoleRequest>,
720) -> Result<Json<ApiResponse<()>>, StatusCode> {
721 let duration_seconds = (request.duration_minutes.unwrap_or(30) * 60) as u64;
722
723 match state
724 .authorization_service
725 .elevate_role(
726 &auth_token.user_id,
727 &request.target_role,
728 Some(duration_seconds),
729 )
730 .await
731 {
732 Ok(_) => {
733 info!(
734 "Role elevation granted to {}: {} for {} minutes - {}",
735 auth_token.user_id,
736 request.target_role,
737 request.duration_minutes.unwrap_or(30),
738 request.justification
739 );
740 Ok(Json(ApiResponse::success(())))
741 }
742 Err(e) => {
743 warn!("Role elevation failed: {}", e);
744 Ok(Json(ApiResponse::<()>::error_with_message_typed(
745 "ELEVATION_FAILED",
746 "Failed to elevate role",
747 )))
748 }
749 }
750}
751
752pub async fn get_audit_logs(
759 State(state): State<ApiState>,
760 Extension(auth_token): Extension<AuthToken>,
761 Query(query): Query<AuditQuery>,
762) -> Result<Json<ApiResponse<AuditLogResponse>>, StatusCode> {
763 if !auth_token.has_permission("read:audit_logs") {
765 return Ok(Json(
766 ApiResponse::<AuditLogResponse>::forbidden_with_message_typed(
767 "Insufficient permissions to read audit logs",
768 ),
769 ));
770 }
771
772 let per_page = query.per_page.unwrap_or(20) as usize;
773 let page = query.page.unwrap_or(1) as usize;
774 let offset = (page.saturating_sub(1)) * per_page;
775 let limit = Some(offset + per_page);
776
777 let logs = match state
778 .auth_framework
779 .get_permission_audit_logs(
780 query.user_id.as_deref(),
781 query.action.as_deref(),
782 query.resource.as_deref(),
783 limit,
784 )
785 .await
786 {
787 Ok(l) => l,
788 Err(e) => {
789 warn!("Failed to retrieve audit logs: {}", e);
790 return Ok(Json(
791 ApiResponse::<AuditLogResponse>::error_with_message_typed(
792 "AUDIT_QUERY_ERROR",
793 "Failed to retrieve audit logs",
794 ),
795 ));
796 }
797 };
798
799 let page_entries: Vec<AuditEntryResponse> = logs
801 .iter()
802 .skip(offset)
803 .take(per_page)
804 .enumerate()
805 .map(|(i, log_line)| {
806 let action = log_line
809 .split_whitespace()
810 .nth(1)
811 .unwrap_or("unknown")
812 .to_string();
813 let user_id = log_line
814 .split("user=")
815 .nth(1)
816 .and_then(|s| s.split_whitespace().next())
817 .map(|s| s.to_string());
818 let result = if log_line.contains("Failure") || log_line.contains("Denied") {
819 "denied"
820 } else {
821 "granted"
822 }
823 .to_string();
824 let description = log_line
825 .split_once(" - ")
826 .map(|x| x.1)
827 .unwrap_or(log_line.as_str())
828 .to_string();
829
830 AuditEntryResponse {
831 id: format!("log-{}", offset + i),
832 user_id,
833 action,
834 resource: query.resource.clone(),
835 result,
836 context: HashMap::new(),
837 timestamp: chrono::Utc::now(), description,
839 }
840 })
841 .collect();
842
843 let total_count = logs.len() as u64;
844
845 let response = AuditLogResponse {
846 entries: page_entries,
847 total_count,
848 page: page as u32,
849 per_page: per_page as u32,
850 };
851
852 Ok(Json(ApiResponse::success(response)))
853}
854
855#[cfg(test)]
856mod tests {
857 use super::*;
858
859 fn create_test_token(permissions: Vec<&str>) -> AuthToken {
861 use crate::tokens::TokenMetadata;
862 use chrono::Utc;
863
864 AuthToken {
865 token_id: "test_token_123".to_string(),
866 user_id: "test_user".to_string(),
867 access_token: "test_access_token".to_string(),
868 token_type: Some("bearer".to_string()),
869 subject: Some("test_user".to_string()),
870 issuer: Some("auth-framework".to_string()),
871 refresh_token: Some("test_refresh_token".to_string()),
872 issued_at: Utc::now(),
873 expires_at: Utc::now() + chrono::Duration::hours(1),
874 scopes: vec!["read".to_string(), "write".to_string()].into(),
875 auth_method: "password".to_string(),
876 client_id: Some("test_client".to_string()),
877 user_profile: None,
878 permissions: permissions
879 .into_iter()
880 .map(|s| s.to_string())
881 .collect::<Vec<_>>()
882 .into(),
883 roles: vec!["admin".to_string()].into(),
884 metadata: TokenMetadata::default(),
885 }
886 }
887
888 #[tokio::test]
889 async fn test_create_role_unauthorized() {
890 let token = create_test_token(vec!["read:users"]);
891 assert!(
892 !token.permissions.contains(&"admin:roles:write".to_string()),
893 "token without admin:roles:write should not be authorized to create roles"
894 );
895 }
896
897 #[tokio::test]
898 async fn test_create_role_success() {
899 let token = create_test_token(vec!["admin:roles:write", "read:users"]);
900 assert!(
901 token.permissions.contains(&"admin:roles:write".to_string()),
902 "token with admin:roles:write should be authorized to create roles"
903 );
904 }
905
906 #[tokio::test]
907 async fn test_permission_check() {
908 let token = create_test_token(vec!["read:users", "write:users"]);
909 assert_eq!(token.permissions.len(), 2);
910 assert!(token.permissions.contains(&"read:users".to_string()));
911 assert!(token.permissions.contains(&"write:users".to_string()));
912 assert!(!token.permissions.contains(&"admin:delete".to_string()));
913 }
914}