use crate::service::auth::authorizer::{iam_resource_arn, Authorizer};
use crate::store::traits::{GroupStore, PolicyStore, RoleStore, UserStore};
use crate::wami::policies::attachment::*;
use std::sync::Arc;
use tokio::sync::RwLock;
use wami_core::actions::WamiAction;
use wami_core::context::WamiContext;
use wami_core::error::{AmiError, Result};
pub trait AttachmentServiceStore: UserStore + GroupStore + RoleStore + PolicyStore {}
impl<T> AttachmentServiceStore for T where T: UserStore + GroupStore + RoleStore + PolicyStore {}
#[wami_macros::service(
store_trait = "crate::service::policies::attachment::AttachmentServiceStore",
generate_new = false
)]
pub struct AttachmentService<S> {
store: Arc<RwLock<S>>,
authz: Option<Arc<dyn Authorizer>>,
}
impl<S> AttachmentService<S>
where
S: AttachmentServiceStore,
{
pub fn new(store: Arc<RwLock<S>>) -> Self {
Self { store, authz: None }
}
pub fn with_authorizer(store: Arc<RwLock<S>>, authz: Arc<dyn Authorizer>) -> Self {
Self {
store,
authz: Some(authz),
}
}
async fn guard(
&self,
context: &WamiContext,
action: WamiAction,
resource_type: &str,
resource_id: &str,
) -> Result<()> {
if let Some(authz) = &self.authz {
let arn = iam_resource_arn(context, resource_type, resource_id)?;
authz.check_or_deny(context, action.as_str(), &arn).await?;
}
Ok(())
}
pub async fn attach_user_policy(
&self,
context: &WamiContext,
request: AttachUserPolicyRequest,
) -> Result<AttachUserPolicyResponse> {
self.guard(
context,
WamiAction::IamAttachPolicy,
"user",
&request.user_name,
)
.await?;
let mut store = self.write_store().await;
store
.get_user(&request.user_name)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("User: {}", request.user_name),
})?;
let policy = store
.get_policy(&request.policy_arn)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("Policy: {}", request.policy_arn),
})?;
if !policy.is_attachable {
return Err(AmiError::InvalidParameter {
message: format!("Policy {} is not attachable", request.policy_arn),
});
}
store
.attach_user_policy(&request.user_name, &request.policy_arn)
.await?;
let mut updated_policy = policy.clone();
updated_policy.attachment_count += 1;
store.update_policy(updated_policy).await?;
Ok(AttachUserPolicyResponse {
message: format!(
"Policy {} attached to user {}",
request.policy_arn, request.user_name
),
})
}
pub async fn detach_user_policy(
&self,
context: &WamiContext,
request: DetachUserPolicyRequest,
) -> Result<DetachUserPolicyResponse> {
self.guard(
context,
WamiAction::IamDetachPolicy,
"user",
&request.user_name,
)
.await?;
let mut store = self.write_store().await;
store
.get_user(&request.user_name)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("User: {}", request.user_name),
})?;
store
.detach_user_policy(&request.user_name, &request.policy_arn)
.await?;
if let Some(policy) = store.get_policy(&request.policy_arn).await? {
let mut updated_policy = policy.clone();
updated_policy.attachment_count = updated_policy.attachment_count.saturating_sub(1);
store.update_policy(updated_policy).await?;
}
Ok(DetachUserPolicyResponse {
message: format!(
"Policy {} detached from user {}",
request.policy_arn, request.user_name
),
})
}
pub async fn list_attached_user_policies(
&self,
context: &WamiContext,
request: ListAttachedUserPoliciesRequest,
) -> Result<ListAttachedUserPoliciesResponse> {
self.guard(
context,
WamiAction::IamReadPolicy,
"user",
&request.user_name,
)
.await?;
let store = self.read_store().await;
store
.get_user(&request.user_name)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("User: {}", request.user_name),
})?;
let policy_arns = store
.list_attached_user_policies(&request.user_name)
.await?;
let mut attached_policies = Vec::new();
for arn in policy_arns {
if let Some(policy) = store.get_policy(&arn).await? {
attached_policies.push(AttachedPolicy {
policy_name: policy.policy_name,
policy_arn: policy.arn,
});
}
}
Ok(ListAttachedUserPoliciesResponse { attached_policies })
}
pub async fn attach_group_policy(
&self,
context: &WamiContext,
request: AttachGroupPolicyRequest,
) -> Result<AttachGroupPolicyResponse> {
self.guard(
context,
WamiAction::IamAttachPolicy,
"group",
&request.group_name,
)
.await?;
let mut store = self.write_store().await;
store
.get_group(&request.group_name)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("Group: {}", request.group_name),
})?;
let policy = store
.get_policy(&request.policy_arn)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("Policy: {}", request.policy_arn),
})?;
if !policy.is_attachable {
return Err(AmiError::InvalidParameter {
message: format!("Policy {} is not attachable", request.policy_arn),
});
}
store
.attach_group_policy(&request.group_name, &request.policy_arn)
.await?;
let mut updated_policy = policy.clone();
updated_policy.attachment_count += 1;
store.update_policy(updated_policy).await?;
Ok(AttachGroupPolicyResponse {
message: format!(
"Policy {} attached to group {}",
request.policy_arn, request.group_name
),
})
}
pub async fn detach_group_policy(
&self,
context: &WamiContext,
request: DetachGroupPolicyRequest,
) -> Result<DetachGroupPolicyResponse> {
self.guard(
context,
WamiAction::IamDetachPolicy,
"group",
&request.group_name,
)
.await?;
let mut store = self.write_store().await;
store
.get_group(&request.group_name)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("Group: {}", request.group_name),
})?;
store
.detach_group_policy(&request.group_name, &request.policy_arn)
.await?;
if let Some(policy) = store.get_policy(&request.policy_arn).await? {
let mut updated_policy = policy.clone();
updated_policy.attachment_count = updated_policy.attachment_count.saturating_sub(1);
store.update_policy(updated_policy).await?;
}
Ok(DetachGroupPolicyResponse {
message: format!(
"Policy {} detached from group {}",
request.policy_arn, request.group_name
),
})
}
pub async fn list_attached_group_policies(
&self,
context: &WamiContext,
request: ListAttachedGroupPoliciesRequest,
) -> Result<ListAttachedGroupPoliciesResponse> {
self.guard(
context,
WamiAction::IamReadPolicy,
"group",
&request.group_name,
)
.await?;
let store = self.read_store().await;
store
.get_group(&request.group_name)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("Group: {}", request.group_name),
})?;
let policy_arns = store
.list_attached_group_policies(&request.group_name)
.await?;
let mut attached_policies = Vec::new();
for arn in policy_arns {
if let Some(policy) = store.get_policy(&arn).await? {
attached_policies.push(AttachedPolicy {
policy_name: policy.policy_name,
policy_arn: policy.arn,
});
}
}
Ok(ListAttachedGroupPoliciesResponse { attached_policies })
}
pub async fn attach_role_policy(
&self,
context: &WamiContext,
request: AttachRolePolicyRequest,
) -> Result<AttachRolePolicyResponse> {
self.guard(
context,
WamiAction::IamAttachPolicy,
"role",
&request.role_name,
)
.await?;
let mut store = self.write_store().await;
store
.get_role(&request.role_name)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("Role: {}", request.role_name),
})?;
let policy = store
.get_policy(&request.policy_arn)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("Policy: {}", request.policy_arn),
})?;
if !policy.is_attachable {
return Err(AmiError::InvalidParameter {
message: format!("Policy {} is not attachable", request.policy_arn),
});
}
store
.attach_role_policy(&request.role_name, &request.policy_arn)
.await?;
let mut updated_policy = policy.clone();
updated_policy.attachment_count += 1;
store.update_policy(updated_policy).await?;
Ok(AttachRolePolicyResponse {
message: format!(
"Policy {} attached to role {}",
request.policy_arn, request.role_name
),
})
}
pub async fn detach_role_policy(
&self,
context: &WamiContext,
request: DetachRolePolicyRequest,
) -> Result<DetachRolePolicyResponse> {
self.guard(
context,
WamiAction::IamDetachPolicy,
"role",
&request.role_name,
)
.await?;
let mut store = self.write_store().await;
store
.get_role(&request.role_name)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("Role: {}", request.role_name),
})?;
store
.detach_role_policy(&request.role_name, &request.policy_arn)
.await?;
if let Some(policy) = store.get_policy(&request.policy_arn).await? {
let mut updated_policy = policy.clone();
updated_policy.attachment_count = updated_policy.attachment_count.saturating_sub(1);
store.update_policy(updated_policy).await?;
}
Ok(DetachRolePolicyResponse {
message: format!(
"Policy {} detached from role {}",
request.policy_arn, request.role_name
),
})
}
pub async fn list_attached_role_policies(
&self,
context: &WamiContext,
request: ListAttachedRolePoliciesRequest,
) -> Result<ListAttachedRolePoliciesResponse> {
self.guard(
context,
WamiAction::IamReadPolicy,
"role",
&request.role_name,
)
.await?;
let store = self.read_store().await;
store
.get_role(&request.role_name)
.await?
.ok_or_else(|| AmiError::ResourceNotFound {
resource: format!("Role: {}", request.role_name),
})?;
let policy_arns = store
.list_attached_role_policies(&request.role_name)
.await?;
let mut attached_policies = Vec::new();
for arn in policy_arns {
if let Some(policy) = store.get_policy(&arn).await? {
attached_policies.push(AttachedPolicy {
policy_name: policy.policy_name,
policy_arn: policy.arn,
});
}
}
Ok(ListAttachedRolePoliciesResponse { attached_policies })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::service::auth::decision::{Decision, DenyReason};
use crate::store::memory::InMemoryWamiStore;
use crate::wami::identity::group::builder::build_group;
use crate::wami::identity::role::builder::build_role;
use crate::wami::identity::user::builder::build_user;
use crate::wami::policies::policy::builder::build_policy;
use std::sync::Arc;
use wami_core::arn::{TenantPath, WamiArn};
use wami_core::context::WamiContext;
async fn create_test_context() -> WamiContext {
WamiContext::builder()
.instance_id("123456789012")
.tenant_path(TenantPath::single(0))
.caller_arn(
WamiArn::builder()
.service(crate::arn::Service::Iam)
.tenant_path(TenantPath::single(0))
.wami_instance("123456789012")
.resource("user", "admin")
.build()
.unwrap(),
)
.is_root(false)
.build()
.unwrap()
}
#[tokio::test]
async fn test_attach_user_policy() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service = AttachmentService::new(store.clone());
let context = create_test_context().await;
let user = build_user("alice".to_string(), Some("/".to_string()), &context).unwrap();
let _created_user = store.write().await.create_user(user).await.unwrap();
let policy = build_policy(
"TestPolicy".to_string(),
r#"{"Version":"2012-10-17","Statement":[]}"#.to_string(),
None,
None,
None,
&context,
)
.unwrap();
let created_policy = store.write().await.create_policy(policy).await.unwrap();
let request = AttachUserPolicyRequest {
user_name: "alice".to_string(),
policy_arn: created_policy.arn.clone(),
};
let response = service.attach_user_policy(&context, request).await.unwrap();
assert!(response.message.contains("attached"));
let list_request = ListAttachedUserPoliciesRequest {
user_name: "alice".to_string(),
};
let list_response = service
.list_attached_user_policies(&context, list_request)
.await
.unwrap();
assert_eq!(list_response.attached_policies.len(), 1);
assert_eq!(
list_response.attached_policies[0].policy_arn,
created_policy.arn
);
}
#[tokio::test]
async fn test_detach_user_policy() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service = AttachmentService::new(store.clone());
let context = create_test_context().await;
let user = build_user("alice".to_string(), Some("/".to_string()), &context).unwrap();
let _created_user = store.write().await.create_user(user).await.unwrap();
let policy = build_policy(
"TestPolicy".to_string(),
r#"{"Version":"2012-10-17","Statement":[]}"#.to_string(),
None,
None,
None,
&context,
)
.unwrap();
let created_policy = store.write().await.create_policy(policy).await.unwrap();
let attach_request = AttachUserPolicyRequest {
user_name: "alice".to_string(),
policy_arn: created_policy.arn.clone(),
};
service
.attach_user_policy(&context, attach_request)
.await
.unwrap();
let detach_request = DetachUserPolicyRequest {
user_name: "alice".to_string(),
policy_arn: created_policy.arn.clone(),
};
let response = service
.detach_user_policy(&context, detach_request)
.await
.unwrap();
assert!(response.message.contains("detached"));
let list_request = ListAttachedUserPoliciesRequest {
user_name: "alice".to_string(),
};
let list_response = service
.list_attached_user_policies(&context, list_request)
.await
.unwrap();
assert_eq!(list_response.attached_policies.len(), 0);
}
#[tokio::test]
async fn test_attach_group_policy() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service = AttachmentService::new(store.clone());
let context = create_test_context().await;
let group = build_group("developers".to_string(), Some("/".to_string()), &context).unwrap();
let _created_group = store.write().await.create_group(group).await.unwrap();
let policy = build_policy(
"TestPolicy".to_string(),
r#"{"Version":"2012-10-17","Statement":[]}"#.to_string(),
None,
None,
None,
&context,
)
.unwrap();
let created_policy = store.write().await.create_policy(policy).await.unwrap();
let request = AttachGroupPolicyRequest {
group_name: "developers".to_string(),
policy_arn: created_policy.arn.clone(),
};
let response = service
.attach_group_policy(&context, request)
.await
.unwrap();
assert!(response.message.contains("attached"));
}
#[tokio::test]
async fn test_attach_role_policy() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service = AttachmentService::new(store.clone());
let context = create_test_context().await;
let role = build_role(
"AdminRole".to_string(),
r#"{"Version":"2012-10-17","Statement":[]}"#.to_string(),
Some("/".to_string()),
None,
None,
&context,
)
.unwrap();
let _created_role = store.write().await.create_role(role).await.unwrap();
let policy = build_policy(
"TestPolicy".to_string(),
r#"{"Version":"2012-10-17","Statement":[]}"#.to_string(),
None,
None,
None,
&context,
)
.unwrap();
let created_policy = store.write().await.create_policy(policy).await.unwrap();
let request = AttachRolePolicyRequest {
role_name: "AdminRole".to_string(),
policy_arn: created_policy.arn.clone(),
};
let response = service.attach_role_policy(&context, request).await.unwrap();
assert!(response.message.contains("attached"));
}
#[tokio::test]
async fn test_list_and_detach_group_policy() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service = AttachmentService::new(store.clone());
let context = create_test_context().await;
let group = build_group("developers".to_string(), Some("/".to_string()), &context).unwrap();
store.write().await.create_group(group).await.unwrap();
let policy = build_policy(
"TestPolicy".to_string(),
r#"{"Version":"2012-10-17","Statement":[]}"#.to_string(),
None,
None,
None,
&context,
)
.unwrap();
let created_policy = store.write().await.create_policy(policy).await.unwrap();
service
.attach_group_policy(
&context,
AttachGroupPolicyRequest {
group_name: "developers".to_string(),
policy_arn: created_policy.arn.clone(),
},
)
.await
.unwrap();
let listed = service
.list_attached_group_policies(
&context,
ListAttachedGroupPoliciesRequest {
group_name: "developers".to_string(),
},
)
.await
.unwrap();
assert_eq!(listed.attached_policies.len(), 1);
assert_eq!(listed.attached_policies[0].policy_arn, created_policy.arn);
service
.detach_group_policy(
&context,
DetachGroupPolicyRequest {
group_name: "developers".to_string(),
policy_arn: created_policy.arn.clone(),
},
)
.await
.unwrap();
let after = service
.list_attached_group_policies(
&context,
ListAttachedGroupPoliciesRequest {
group_name: "developers".to_string(),
},
)
.await
.unwrap();
assert!(after.attached_policies.is_empty());
}
#[tokio::test]
async fn test_list_and_detach_role_policy() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service = AttachmentService::new(store.clone());
let context = create_test_context().await;
let role = build_role(
"AdminRole".to_string(),
r#"{"Version":"2012-10-17","Statement":[]}"#.to_string(),
Some("/".to_string()),
None,
None,
&context,
)
.unwrap();
store.write().await.create_role(role).await.unwrap();
let policy = build_policy(
"TestPolicy".to_string(),
r#"{"Version":"2012-10-17","Statement":[]}"#.to_string(),
None,
None,
None,
&context,
)
.unwrap();
let created_policy = store.write().await.create_policy(policy).await.unwrap();
service
.attach_role_policy(
&context,
AttachRolePolicyRequest {
role_name: "AdminRole".to_string(),
policy_arn: created_policy.arn.clone(),
},
)
.await
.unwrap();
let listed = service
.list_attached_role_policies(
&context,
ListAttachedRolePoliciesRequest {
role_name: "AdminRole".to_string(),
},
)
.await
.unwrap();
assert_eq!(listed.attached_policies.len(), 1);
assert_eq!(listed.attached_policies[0].policy_arn, created_policy.arn);
service
.detach_role_policy(
&context,
DetachRolePolicyRequest {
role_name: "AdminRole".to_string(),
policy_arn: created_policy.arn.clone(),
},
)
.await
.unwrap();
let after = service
.list_attached_role_policies(
&context,
ListAttachedRolePoliciesRequest {
role_name: "AdminRole".to_string(),
},
)
.await
.unwrap();
assert!(after.attached_policies.is_empty());
}
#[tokio::test]
async fn test_attach_policy_user_not_found() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service = AttachmentService::new(store.clone());
let context = create_test_context().await;
let policy = build_policy(
"TestPolicy".to_string(),
r#"{"Version":"2012-10-17","Statement":[]}"#.to_string(),
None,
None,
None,
&context,
)
.unwrap();
let created_policy = store.write().await.create_policy(policy).await.unwrap();
let request = AttachUserPolicyRequest {
user_name: "nonexistent".to_string(),
policy_arn: created_policy.arn,
};
let result = service.attach_user_policy(&context, request).await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
AmiError::ResourceNotFound { .. }
));
}
#[tokio::test]
async fn test_attach_policy_not_found() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service = AttachmentService::new(store.clone());
let context = create_test_context().await;
let user = build_user("alice".to_string(), Some("/".to_string()), &context).unwrap();
let _created_user = store.write().await.create_user(user).await.unwrap();
let request = AttachUserPolicyRequest {
user_name: "alice".to_string(),
policy_arn: "arn:wami:.*:0:wami:123456789012:policy/nonexistent".to_string(),
};
let result = service.attach_user_policy(&context, request).await;
assert!(result.is_err());
}
use crate::service::auth::authorizer::Authorizer;
use async_trait::async_trait;
struct DenyAllAuthorizer;
#[async_trait]
impl Authorizer for DenyAllAuthorizer {
async fn authorize(
&self,
_ctx: &WamiContext,
_action: &str,
_arn: &WamiArn,
) -> wami_core::error::Result<Decision> {
Ok(Decision::Deny(DenyReason::NoMatch))
}
}
#[tokio::test]
async fn test_guard_attach_user_policy_denied() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service =
AttachmentService::with_authorizer(store.clone(), Arc::new(DenyAllAuthorizer));
let context = create_test_context().await;
let request = AttachUserPolicyRequest {
user_name: "alice".to_string(),
policy_arn: "arn:wami:iam:0:wami:123456789012:policy/test".to_string(),
};
let result = service.attach_user_policy(&context, request).await;
assert!(matches!(
result,
Err(wami_core::error::AmiError::AccessDenied { .. })
));
}
#[tokio::test]
async fn test_guard_detach_user_policy_denied() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service =
AttachmentService::with_authorizer(store.clone(), Arc::new(DenyAllAuthorizer));
let context = create_test_context().await;
let request = DetachUserPolicyRequest {
user_name: "alice".to_string(),
policy_arn: "arn:wami:iam:0:wami:123456789012:policy/test".to_string(),
};
let result = service.detach_user_policy(&context, request).await;
assert!(matches!(
result,
Err(wami_core::error::AmiError::AccessDenied { .. })
));
}
#[tokio::test]
async fn test_guard_list_attached_user_policies_denied() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service =
AttachmentService::with_authorizer(store.clone(), Arc::new(DenyAllAuthorizer));
let context = create_test_context().await;
let request = ListAttachedUserPoliciesRequest {
user_name: "alice".to_string(),
};
let result = service.list_attached_user_policies(&context, request).await;
assert!(matches!(
result,
Err(wami_core::error::AmiError::AccessDenied { .. })
));
}
#[tokio::test]
async fn test_guard_attach_group_policy_denied() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service =
AttachmentService::with_authorizer(store.clone(), Arc::new(DenyAllAuthorizer));
let context = create_test_context().await;
let request = AttachGroupPolicyRequest {
group_name: "admins".to_string(),
policy_arn: "arn:wami:iam:0:wami:123456789012:policy/test".to_string(),
};
let result = service.attach_group_policy(&context, request).await;
assert!(matches!(
result,
Err(wami_core::error::AmiError::AccessDenied { .. })
));
}
#[tokio::test]
async fn test_guard_attach_role_policy_denied() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service =
AttachmentService::with_authorizer(store.clone(), Arc::new(DenyAllAuthorizer));
let context = create_test_context().await;
let request = AttachRolePolicyRequest {
role_name: "admin-role".to_string(),
policy_arn: "arn:wami:iam:0:wami:123456789012:policy/test".to_string(),
};
let result = service.attach_role_policy(&context, request).await;
assert!(matches!(
result,
Err(wami_core::error::AmiError::AccessDenied { .. })
));
}
#[tokio::test]
async fn test_guard_detach_group_policy_denied() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service =
AttachmentService::with_authorizer(store.clone(), Arc::new(DenyAllAuthorizer));
let context = create_test_context().await;
let request = DetachGroupPolicyRequest {
group_name: "admins".to_string(),
policy_arn: "arn:wami:iam:0:wami:123456789012:policy/test".to_string(),
};
assert!(matches!(
service.detach_group_policy(&context, request).await,
Err(wami_core::error::AmiError::AccessDenied { .. })
));
}
#[tokio::test]
async fn test_guard_list_attached_group_policies_denied() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service =
AttachmentService::with_authorizer(store.clone(), Arc::new(DenyAllAuthorizer));
let context = create_test_context().await;
let request = ListAttachedGroupPoliciesRequest {
group_name: "admins".to_string(),
};
assert!(matches!(
service
.list_attached_group_policies(&context, request)
.await,
Err(wami_core::error::AmiError::AccessDenied { .. })
));
}
#[tokio::test]
async fn test_guard_detach_role_policy_denied() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service =
AttachmentService::with_authorizer(store.clone(), Arc::new(DenyAllAuthorizer));
let context = create_test_context().await;
let request = DetachRolePolicyRequest {
role_name: "admin-role".to_string(),
policy_arn: "arn:wami:iam:0:wami:123456789012:policy/test".to_string(),
};
assert!(matches!(
service.detach_role_policy(&context, request).await,
Err(wami_core::error::AmiError::AccessDenied { .. })
));
}
#[tokio::test]
async fn test_guard_list_attached_role_policies_denied() {
let store = Arc::new(RwLock::new(InMemoryWamiStore::new()));
let service =
AttachmentService::with_authorizer(store.clone(), Arc::new(DenyAllAuthorizer));
let context = create_test_context().await;
let request = ListAttachedRolePoliciesRequest {
role_name: "admin-role".to_string(),
};
assert!(matches!(
service.list_attached_role_policies(&context, request).await,
Err(wami_core::error::AmiError::AccessDenied { .. })
));
}
}