181 lines
6.1 KiB
Rust
181 lines
6.1 KiB
Rust
use axum::body::Body;
|
|
use axum::http::{Request, StatusCode};
|
|
use iam_service::presentation::http::api;
|
|
use iam_service::presentation::http::state::AppState;
|
|
use iam_service::infrastructure::repositories::tenant_config_repo::TenantConfigRepoPg;
|
|
use iam_service::models::CreateTenantRequest;
|
|
use iam_service::models::CreateUserRequest;
|
|
use iam_service::application::services::{
|
|
AppService, AuthService, AuthorizationService, ClientService, PermissionService, RoleService,
|
|
TenantService, UserService,
|
|
};
|
|
use redis::aio::ConnectionManager;
|
|
use sqlx::PgPool;
|
|
use tower::ServiceExt;
|
|
use uuid::Uuid;
|
|
|
|
#[tokio::test]
|
|
async fn code2token_modes_requirements() -> Result<(), Box<dyn std::error::Error>> {
|
|
let database_url = match std::env::var("DATABASE_URL") {
|
|
Ok(v) if !v.trim().is_empty() => v,
|
|
_ => return Ok(()),
|
|
};
|
|
let redis_url = match std::env::var("REDIS_URL") {
|
|
Ok(v) if !v.trim().is_empty() => v,
|
|
_ => return Ok(()),
|
|
};
|
|
let auth_code_jwt_secret = match std::env::var("AUTH_CODE_JWT_SECRET") {
|
|
Ok(v) if !v.trim().is_empty() => v,
|
|
_ => return Ok(()),
|
|
};
|
|
let internal_psk = match std::env::var("INTERNAL_EXCHANGE_PSK") {
|
|
Ok(v) if !v.trim().is_empty() => v,
|
|
_ => return Ok(()),
|
|
};
|
|
|
|
let pool = PgPool::connect(&database_url).await?;
|
|
|
|
let redis = redis::Client::open(redis_url)?;
|
|
let redis: ConnectionManager = ConnectionManager::new(redis).await?;
|
|
|
|
let auth_service = AuthService::new(pool.clone(), "test".to_string());
|
|
let client_service = ClientService::new(pool.clone(), 30);
|
|
let user_service = UserService::new(pool.clone());
|
|
let role_service = RoleService::new(pool.clone());
|
|
let tenant_service = TenantService::new(pool.clone());
|
|
let authorization_service = AuthorizationService::new(pool.clone());
|
|
let app_service = AppService::new(pool.clone());
|
|
let permission_service = PermissionService::new(pool.clone());
|
|
|
|
let tenant_config_repo = std::sync::Arc::new(TenantConfigRepoPg::new(pool.clone()));
|
|
|
|
let state = AppState {
|
|
auth_service: auth_service.clone(),
|
|
client_service: client_service.clone(),
|
|
user_service,
|
|
role_service,
|
|
tenant_service: tenant_service.clone(),
|
|
authorization_service,
|
|
app_service,
|
|
permission_service,
|
|
redis,
|
|
auth_code_jwt_secret: auth_code_jwt_secret.clone(),
|
|
tenant_config_repo,
|
|
};
|
|
|
|
let tenant = tenant_service
|
|
.create_tenant(CreateTenantRequest {
|
|
name: format!("t-{}", Uuid::new_v4()),
|
|
config: None,
|
|
})
|
|
.await?;
|
|
|
|
let email = format!("u{}@example.com", Uuid::new_v4());
|
|
let password = "P@ssw0rd123!".to_string();
|
|
let _user = auth_service
|
|
.register(
|
|
tenant.id,
|
|
CreateUserRequest {
|
|
email: email.clone(),
|
|
password: password.clone(),
|
|
},
|
|
)
|
|
.await?;
|
|
|
|
let client_id = format!("cms{}", Uuid::new_v4().to_string().replace('-', ""));
|
|
let client_id = &client_id[..std::cmp::min(24, client_id.len())];
|
|
let redirect_uri = "http://localhost:5031/auth/callback".to_string();
|
|
let client_secret = client_service
|
|
.create_client(
|
|
client_id.to_string(),
|
|
Some("CMS".to_string()),
|
|
Some(vec![redirect_uri.clone()]),
|
|
)
|
|
.await?;
|
|
|
|
let app = api::build_app(state);
|
|
|
|
let login_code_req = serde_json::json!({
|
|
"clientId": client_id,
|
|
"redirectUri": redirect_uri,
|
|
"email": email,
|
|
"password": password
|
|
});
|
|
let resp = app
|
|
.clone()
|
|
.oneshot(
|
|
Request::builder()
|
|
.method("POST")
|
|
.uri("/api/v1/auth/login-code")
|
|
.header("Content-Type", "application/json")
|
|
.header("X-Tenant-ID", tenant.id.to_string())
|
|
.body(Body::from(login_code_req.to_string()))?,
|
|
)
|
|
.await?;
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
let body = axum::body::to_bytes(resp.into_body(), usize::MAX).await?;
|
|
let v: serde_json::Value = serde_json::from_slice(&body)?;
|
|
let redirect_to = v["data"]["redirectTo"].as_str().unwrap_or_default();
|
|
let url = url::Url::parse(redirect_to)?;
|
|
let code = url
|
|
.query_pairs()
|
|
.find(|(k, _)| k == "code")
|
|
.map(|(_, v)| v.to_string())
|
|
.unwrap_or_default();
|
|
assert!(!code.is_empty());
|
|
|
|
let code2token_req = serde_json::json!({
|
|
"code": code,
|
|
"clientId": client_id,
|
|
"clientSecret": "wrong"
|
|
});
|
|
let resp = app
|
|
.clone()
|
|
.oneshot(
|
|
Request::builder()
|
|
.method("POST")
|
|
.uri("/api/v1/auth/code2token")
|
|
.header("Content-Type", "application/json")
|
|
.header("X-Tenant-ID", tenant.id.to_string())
|
|
.body(Body::from(code2token_req.to_string()))?,
|
|
)
|
|
.await?;
|
|
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
|
|
|
|
let code2token_req_missing_tenant = serde_json::json!({
|
|
"code": url.query_pairs().find(|(k, _)| k == "code").unwrap().1,
|
|
"clientId": client_id,
|
|
"clientSecret": client_secret
|
|
});
|
|
let resp = app
|
|
.clone()
|
|
.oneshot(
|
|
Request::builder()
|
|
.method("POST")
|
|
.uri("/api/v1/auth/code2token")
|
|
.header("Content-Type", "application/json")
|
|
.body(Body::from(code2token_req_missing_tenant.to_string()))?,
|
|
)
|
|
.await?;
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
|
|
let code2token_req_internal = serde_json::json!({
|
|
"code": url.query_pairs().find(|(k, _)| k == "code").unwrap().1,
|
|
"clientId": client_id,
|
|
"clientSecret": client_secret
|
|
});
|
|
let resp = app
|
|
.oneshot(
|
|
Request::builder()
|
|
.method("POST")
|
|
.uri("/api/v1/internal/auth/code2token")
|
|
.header("Content-Type", "application/json")
|
|
.header("X-Internal-Token", internal_psk)
|
|
.body(Body::from(code2token_req_internal.to_string()))?,
|
|
)
|
|
.await?;
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
|
|
Ok(())
|
|
}
|