Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions acton-service/src/accounts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,11 +712,10 @@ mod audit_integration {
}),
),
AccountEvent::PasswordChanged { ref account_id } => (
AuditEventKind::AccountUpdated,
AuditSeverity::Informational,
AuditEventKind::AuthPasswordChanged,
AuditSeverity::Notice,
serde_json::json!({
"account_id": account_id,
"action": "password_changed",
}),
),
AccountEvent::RolesUpdated {
Expand Down
78 changes: 77 additions & 1 deletion acton-service/src/middleware/cedar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,35 @@ impl CedarAuthz {
})?
.clone();

#[cfg(feature = "audit")]
let audit_logger = request
.extensions()
.get::<crate::audit::AuditLogger>()
.cloned();
#[cfg(feature = "audit")]
let audit_source = {
use crate::audit::event::AuditSource;
AuditSource {
ip: request
.headers()
.get("x-forwarded-for")
.or_else(|| request.headers().get("x-real-ip"))
.and_then(|v| v.to_str().ok())
.map(|s| s.split(',').next().unwrap_or(s).trim().to_string()),
user_agent: request
.headers()
.get("user-agent")
.and_then(|v| v.to_str().ok())
.map(String::from),
subject: Some(claims.sub.clone()),
request_id: request
.headers()
.get("x-request-id")
.and_then(|v| v.to_str().ok())
.map(String::from),
}
};

// Extract request information
let method = request.method().clone();

Expand All @@ -346,7 +375,21 @@ impl CedarAuthz {
.await?
{
Decision::Allow => Ok(next.run(request).await),
Decision::Deny => Err(Error::Forbidden("Access denied by policy".to_string())),
Decision::Deny => {
#[cfg(feature = "audit")]
if let Some(ref logger) = audit_logger {
if logger.config().audit_auth_events {
logger
.log_auth(
crate::audit::event::AuditEventKind::AuthPermissionDenied,
crate::audit::event::AuditSeverity::Warning,
audit_source,
)
.await;
}
}
Err(Error::Forbidden("Access denied by policy".to_string()))
}
}
}

Expand Down Expand Up @@ -751,6 +794,12 @@ where
})?
.clone();

#[cfg(feature = "audit")]
let audit_logger = req
.extensions()
.get::<crate::audit::AuditLogger>()
.cloned();

// Extract gRPC method path from metadata
let method_path = req
.metadata()
Expand Down Expand Up @@ -786,6 +835,33 @@ where
method = %method_path,
"Cedar policy denied gRPC request"
);
#[cfg(feature = "audit")]
if let Some(ref logger) = audit_logger {
if logger.config().audit_auth_events {
use crate::audit::event::AuditSource;
let source = AuditSource {
ip: None,
user_agent: req
.metadata()
.get("user-agent")
.and_then(|v| v.to_str().ok())
.map(String::from),
subject: Some(claims.sub.clone()),
request_id: req
.metadata()
.get("x-request-id")
.and_then(|v| v.to_str().ok())
.map(String::from),
};
logger
.log_auth(
crate::audit::event::AuditEventKind::AuthPermissionDenied,
crate::audit::event::AuditSeverity::Warning,
source,
)
.await;
}
}
Err(Status::permission_denied("Access denied by policy"))
}
}
Expand Down
59 changes: 54 additions & 5 deletions acton-service/src/middleware/rate_limit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,63 @@ impl RateLimit {
) -> Result<Response, Error> {
#[cfg(feature = "cache")]
{
let method = request.method().as_str();
let path = request.uri().path();
let method = request.method().as_str().to_string();
let path = request.uri().path().to_string();
let claims = request.extensions().get::<Claims>().cloned();

#[cfg(feature = "audit")]
let audit_logger = request
.extensions()
.get::<crate::audit::AuditLogger>()
.cloned();
#[cfg(feature = "audit")]
let audit_source = {
use crate::audit::event::AuditSource;
AuditSource {
ip: request
.headers()
.get("x-forwarded-for")
.or_else(|| request.headers().get("x-real-ip"))
.and_then(|v| v.to_str().ok())
.map(|s| s.split(',').next().unwrap_or(s).trim().to_string()),
user_agent: request
.headers()
.get("user-agent")
.and_then(|v| v.to_str().ok())
.map(String::from),
subject: claims.as_ref().map(|c| c.sub.clone()),
request_id: request
.headers()
.get("x-request-id")
.and_then(|v| v.to_str().ok())
.map(String::from),
}
};

// Check rate limit and get result for headers
let result = rate_limit
.check_rate_limit_with_route(method, path, claims.as_ref())
.await?;
let result = match rate_limit
.check_rate_limit_with_route(&method, &path, claims.as_ref())
.await
{
Ok(r) => r,
Err(e) => {
#[cfg(feature = "audit")]
if matches!(e, Error::RateLimitExceeded) {
if let Some(ref logger) = audit_logger {
if logger.config().audit_auth_events {
logger
.log_auth(
crate::audit::event::AuditEventKind::HttpRequestDenied,
crate::audit::event::AuditSeverity::Warning,
audit_source,
)
.await;
}
}
}
return Err(e);
}
};

// Run the request
let mut response = next.run(request).await;
Expand Down
Loading