summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-02 12:32:27 -0600
committermo khan <mo@mokhan.ca>2025-07-02 12:32:27 -0600
commita577c62277e3d651b66fd68dbe800bf3ab5c4921 (patch)
tree7ae4e79fc84c539c12fb0313d0d3cc929b2e12ae /tests
parentc2b8edab01b23fde6cc196a3349ad6aa19a93299 (diff)
parent0b610d061e45811130d8cf3919037fdc9513e340 (diff)
Merge branch 'rs' into 'main'
Re-write the authorization daemon in rust See merge request gitlab-org/software-supply-chain-security/authorization/authzd!1
Diffstat (limited to 'tests')
-rw-r--r--tests/authorization/cedar_authorizer_test.rs66
-rw-r--r--tests/authorization/check_service_test.rs118
-rw-r--r--tests/authorization/mod.rs3
-rw-r--r--tests/authorization/server_test.rs48
-rw-r--r--tests/integration_test.rs2
-rw-r--r--tests/support/factory_bot.rs58
-rw-r--r--tests/support/mod.rs1
7 files changed, 296 insertions, 0 deletions
diff --git a/tests/authorization/cedar_authorizer_test.rs b/tests/authorization/cedar_authorizer_test.rs
new file mode 100644
index 00000000..76bf06df
--- /dev/null
+++ b/tests/authorization/cedar_authorizer_test.rs
@@ -0,0 +1,66 @@
+#[cfg(test)]
+mod tests {
+ use crate::support::factory_bot::*;
+ use authzd::Authorizer;
+ use envoy_types::pb::envoy::service::auth::v3::attribute_context::HttpRequest;
+ use std::collections::HashMap;
+
+ #[test]
+ fn test_cedar_authorizer_allows_valid_token() {
+ let request = build_request(|item: &mut HttpRequest| {
+ item.headers = build_with(|item: &mut HashMap<String, String>| {
+ item.insert(
+ String::from("authorization"),
+ String::from("Bearer valid-token"),
+ );
+ });
+ });
+
+ assert!(build_cedar_authorizer().authorize(request));
+ }
+
+ #[test]
+ fn test_cedar_authorizer_denies_invalid_token() {
+ let request = build_request(|item: &mut HttpRequest| {
+ item.headers = build_with(|item: &mut HashMap<String, String>| {
+ item.insert(
+ String::from("authorization"),
+ String::from("Bearer invalid-token"),
+ );
+ });
+ });
+
+ assert!(!build_cedar_authorizer().authorize(request));
+ }
+
+ #[test]
+ fn test_cedar_authorizer_denies_missing_header() {
+ let request = build_request(|item: &mut HttpRequest| {
+ item.headers = HashMap::new();
+ });
+
+ assert!(!build_cedar_authorizer().authorize(request));
+ }
+
+ #[test]
+ fn test_cedar_authorizer_allows_static_assets() {
+ let request = build_request(|item: &mut HttpRequest| {
+ item.headers = build_with(|item: &mut HashMap<String, String>| {
+ item.insert(String::from(":path"), String::from("/public/style.css"));
+ });
+ });
+
+ assert!(build_cedar_authorizer().authorize(request));
+ }
+
+ #[test]
+ fn test_cedar_authorizer_allows_js_assets() {
+ let mut headers = HashMap::new();
+ headers.insert(":path".to_string(), "/app.js".to_string());
+ let request = build_request(|item: &mut HttpRequest| {
+ item.headers = headers;
+ });
+
+ assert!(build_cedar_authorizer().authorize(request));
+ }
+}
diff --git a/tests/authorization/check_service_test.rs b/tests/authorization/check_service_test.rs
new file mode 100644
index 00000000..3a225974
--- /dev/null
+++ b/tests/authorization/check_service_test.rs
@@ -0,0 +1,118 @@
+#[cfg(test)]
+mod tests {
+ use crate::support::factory_bot::*;
+ use authzd::CheckService;
+ use envoy_types::ext_authz::v3::pb::Authorization;
+ use envoy_types::pb::envoy::service::auth::v3::attribute_context::HttpRequest;
+ use std::collections::HashMap;
+ use std::sync::Arc;
+
+ fn subject() -> CheckService {
+ CheckService::new(Arc::new(build_cedar_authorizer()))
+ }
+
+ #[tokio::test]
+ async fn test_check_allows_valid_bearer_token() {
+ let request = tonic::Request::new(build_request(|item: &mut HttpRequest| {
+ item.headers = build_headers(vec![(
+ "authorization".to_string(),
+ format!("Bearer {}", String::from("valid-token")),
+ )])
+ }));
+
+ let response = subject().check(request).await;
+ assert!(response.is_ok());
+
+ let check_response = response.unwrap().into_inner();
+ assert!(check_response.status.is_some());
+
+ let status = check_response.status.unwrap();
+ assert_eq!(status.code, tonic::Code::Ok as i32);
+ }
+
+ #[tokio::test]
+ async fn test_check_denies_invalid_bearer_token() {
+ let request = tonic::Request::new(build_request(|item: &mut HttpRequest| {
+ item.headers = HashMap::new();
+ }));
+
+ let response = subject().check(request).await;
+ assert!(response.is_ok());
+
+ let check_response = response.unwrap().into_inner();
+ assert!(check_response.status.is_some());
+
+ let status = check_response.status.unwrap();
+ assert_eq!(status.code, tonic::Code::Unauthenticated as i32);
+ }
+
+ #[tokio::test]
+ async fn test_static_assets() {
+ let static_paths = vec![
+ "app.js",
+ "favicon.ico",
+ "image.jpg",
+ "index.html",
+ "logo.png",
+ "style.css",
+ ];
+
+ for path in static_paths {
+ let request = tonic::Request::new(build_request(|http| {
+ http.headers = build_headers(vec![(":path".to_string(), path.to_string())]);
+ }));
+
+ let response = subject().check(request).await;
+ assert!(response.is_ok());
+
+ let check_response = response.unwrap().into_inner();
+ assert!(check_response.status.is_some());
+
+ let status = check_response.status.unwrap();
+ assert_eq!(status.code, tonic::Code::Ok as i32);
+ }
+ }
+
+ #[tokio::test]
+ async fn test_no_headers() {
+ let request = tonic::Request::new(build_request(|_http| {}));
+
+ let response = subject().check(request).await;
+ assert!(response.is_ok());
+
+ let check_response = response.unwrap().into_inner();
+ assert!(check_response.status.is_some());
+
+ let status = check_response.status.unwrap();
+ assert_eq!(status.code, tonic::Code::Unauthenticated as i32);
+ }
+
+ #[tokio::test]
+ async fn test_table() {
+ let test_cases = vec![
+ ("Bearer valid-token", true),
+ ("Bearer invalid-token", false),
+ ("Basic valid-token", false),
+ ("", false),
+ ];
+
+ for (auth_value, should_succeed) in test_cases {
+ let request = tonic::Request::new(build_request(|item: &mut HttpRequest| {
+ item.headers =
+ build_headers(vec![("authorization".to_string(), auth_value.to_string())]);
+ }));
+
+ let response = subject().check(request).await;
+ assert!(response.is_ok());
+
+ let check_response = response.unwrap().into_inner();
+ let status = check_response.status.unwrap();
+
+ if should_succeed {
+ assert_eq!(status.code, tonic::Code::Ok as i32);
+ } else {
+ assert_eq!(status.code, tonic::Code::Unauthenticated as i32);
+ }
+ }
+ }
+}
diff --git a/tests/authorization/mod.rs b/tests/authorization/mod.rs
new file mode 100644
index 00000000..675247d4
--- /dev/null
+++ b/tests/authorization/mod.rs
@@ -0,0 +1,3 @@
+mod cedar_authorizer_test;
+mod check_service_test;
+mod server_test;
diff --git a/tests/authorization/server_test.rs b/tests/authorization/server_test.rs
new file mode 100644
index 00000000..fe8c8a73
--- /dev/null
+++ b/tests/authorization/server_test.rs
@@ -0,0 +1,48 @@
+#[cfg(test)]
+mod tests {
+ use crate::support::factory_bot::*;
+ use std::net::SocketAddr;
+ use tokio::net::TcpListener;
+
+ async fn available_port() -> SocketAddr {
+ let listener = TcpListener::bind("127.0.0.1:0")
+ .await
+ .expect("Failed to bind to random port");
+ let addr = listener.local_addr().expect("Failed to get local address");
+ drop(listener);
+ addr
+ }
+
+ async fn start_server() -> (SocketAddr, tokio::task::JoinHandle<()>) {
+ let addr = available_port().await;
+ let server = authzd::authorization::Server::default();
+
+ let handle = tokio::spawn(async move {
+ server.serve(addr).await.expect("Failed to start server");
+ });
+
+ tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
+
+ (addr, handle)
+ }
+
+ #[tokio::test]
+ async fn test_health_check_service() {
+ let (addr, server) = start_server().await;
+ let mut client =
+ build_rpc_client(addr, tonic_health::pb::health_client::HealthClient::new).await;
+
+ let request = tonic::Request::new(tonic_health::pb::HealthCheckRequest {
+ service: String::new(),
+ });
+ let response = client.check(request).await;
+
+ assert!(response.is_ok());
+ assert_eq!(
+ response.unwrap().into_inner().status(),
+ tonic_health::pb::health_check_response::ServingStatus::Serving
+ );
+
+ server.abort();
+ }
+}
diff --git a/tests/integration_test.rs b/tests/integration_test.rs
new file mode 100644
index 00000000..c17d8e65
--- /dev/null
+++ b/tests/integration_test.rs
@@ -0,0 +1,2 @@
+mod authorization;
+mod support;
diff --git a/tests/support/factory_bot.rs b/tests/support/factory_bot.rs
new file mode 100644
index 00000000..15c6f1f3
--- /dev/null
+++ b/tests/support/factory_bot.rs
@@ -0,0 +1,58 @@
+use envoy_types::ext_authz::v3::pb::CheckRequest;
+use envoy_types::pb::envoy::service::auth::v3::AttributeContext;
+use envoy_types::pb::envoy::service::auth::v3::attribute_context::{HttpRequest, Request};
+use std::collections::HashMap;
+use std::net::SocketAddr;
+use tonic::transport::Channel;
+
+#[allow(dead_code)]
+pub fn build<T: Default>() -> T {
+ return please::build();
+}
+
+pub fn build_with<T, F>(initializer: F) -> T
+where
+ T: Default,
+ F: std::ops::FnOnce(&mut T),
+{
+ return please::build_with(initializer);
+}
+
+pub fn build_request(f: impl std::ops::FnOnce(&mut HttpRequest)) -> CheckRequest {
+ build_with(|item: &mut CheckRequest| {
+ item.attributes = Some(please::build_with(|item: &mut AttributeContext| {
+ item.request = Some(please::build_with(|item: &mut Request| {
+ item.http = Some(please::build_with(|item: &mut HttpRequest| f(item)));
+ }));
+ }));
+ })
+}
+
+pub fn build_headers(headers: Vec<(String, String)>) -> HashMap<String, String> {
+ return build_with(|item: &mut HashMap<String, String>| {
+ for (key, value) in headers {
+ item.insert(key, value);
+ }
+ });
+}
+
+pub fn build_cedar_authorizer() -> authzd::CedarAuthorizer {
+ let realpath = std::fs::canonicalize("./etc/authzd").unwrap();
+ let path = realpath.as_path();
+ authzd::CedarAuthorizer::new_from(path)
+}
+
+pub async fn build_channel(addr: SocketAddr) -> Channel {
+ Channel::from_shared(format!("http://{}", addr))
+ .expect("Failed to create channel")
+ .connect()
+ .await
+ .expect("Failed to connect to server")
+}
+
+pub async fn build_rpc_client<T, F>(addr: SocketAddr, f: F) -> T
+where
+ F: FnOnce(Channel) -> T,
+{
+ f(build_channel(addr).await)
+}
diff --git a/tests/support/mod.rs b/tests/support/mod.rs
new file mode 100644
index 00000000..5e2a6d78
--- /dev/null
+++ b/tests/support/mod.rs
@@ -0,0 +1 @@
+pub mod factory_bot;