#[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.path = String::from("/"); 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(|item: &mut HttpRequest| { let method = String::from("GET"); let host = String::from("sparkle.staging.runway.gitlab.net"); item.method = method.clone(); item.path = path.to_string(); item.host = host.to_string(); item.headers = build_headers(vec![ (String::from(":path"), path.to_string()), (String::from(":method"), method), (String::from(":authority"), host), ]); })); 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", tonic::Code::Ok), ("Bearer invalid-token", tonic::Code::Unauthenticated), ("Basic valid-token", tonic::Code::Unauthenticated), ("", tonic::Code::Unauthenticated), ]; for (auth_value, expected_status_code) 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(); assert_eq!(status.code, expected_status_code as i32); } } #[tokio::test] async fn test_public_sparkle_endpoints() { // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/application.js"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/callback"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/dashboard", Headers: loggedInHeaders}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/dashboard/nav"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/favicon.ico"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/favicon.png"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/favicon.png"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/health"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/htmx.js"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/index.html"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/logo.png"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/pico.min.css"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/signout"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/sparkles"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "GET", Path: "/vue.global.js"}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "POST", Path: "/sparkles", Headers: loggedInHeaders}}, // {status: tonic::Code::Ok, http: &HTTPRequest{Method: "POST", Path: "/sparkles/restore"}}, // {status: tonic::Code::PermissionDenied, http: &HTTPRequest{Method: "GET", Path: "/dashboard"}}, // {status: tonic::Code::PermissionDenied, http: &HTTPRequest{Method: "GET", Path: "/dashboard", Headers: invalidHeaders}}, // {status: tonic::Code::PermissionDenied, http: &HTTPRequest{Method: "POST", Path: "/sparkles"}}, let test_cases = vec![ ( "GET", "/", "sparkle.staging.runway.gitlab.net", tonic::Code::Ok, ), ( "GET", "/application.js", "sparkle.staging.runway.gitlab.net", tonic::Code::Ok, ), ( "GET", "/callback", "sparkle.staging.runway.gitlab.net", tonic::Code::Ok, ), ]; for (method, path, host, expected_status_code) in test_cases { let request = tonic::Request::new(build_request(|item: &mut HttpRequest| { item.method = method.to_string(); item.path = path.to_string(); item.host = host.to_string(); item.headers = build_headers(vec![ (String::from(":path"), path.to_string()), (String::from(":method"), method.to_string()), (String::from(":authority"), host.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, expected_status_code as i32); } } }