perf(struct): ddd
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use iam_service::models::{CreateAppRequest, ListAppsQuery, RequestAppStatusChangeRequest, UpdateAppRequest};
|
||||
use iam_service::services::AppService;
|
||||
use iam_service::application::services::AppService;
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -90,4 +90,3 @@ async fn app_lifecycle_create_update_disable_approve_delete()
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
180
tests/code2token_modes.rs
Normal file
180
tests/code2token_modes.rs
Normal file
@@ -0,0 +1,180 @@
|
||||
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(())
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use iam_service::services::TenantService;
|
||||
use iam_service::application::services::TenantService;
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use uuid::Uuid;
|
||||
async fn jwks_endpoint_allows_rs256_verification_via_auth_kit() {
|
||||
let app = Router::new().route(
|
||||
"/.well-known/jwks.json",
|
||||
get(iam_service::handlers::jwks_handler),
|
||||
get(iam_service::presentation::http::handlers::jwks::jwks_handler),
|
||||
);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use iam_service::application::services::{AuthService, TenantService, UserService};
|
||||
use iam_service::models::{CreateUserRequest, LoginRequest};
|
||||
use iam_service::services::{AuthService, TenantService, UserService};
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::test]
|
||||
async fn password_reset_self_and_admin_flow()
|
||||
-> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn password_reset_self_and_admin_flow() -> 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(()),
|
||||
@@ -63,11 +62,12 @@ async fn password_reset_self_and_admin_flow()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let active_tokens: i64 =
|
||||
sqlx::query_scalar("SELECT COUNT(1) FROM refresh_tokens WHERE user_id = $1 AND is_revoked = FALSE")
|
||||
.bind(user.id)
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
let active_tokens: i64 = sqlx::query_scalar(
|
||||
"SELECT COUNT(1) FROM refresh_tokens WHERE user_id = $1 AND is_revoked = FALSE",
|
||||
)
|
||||
.bind(user.id)
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
assert!(active_tokens >= 1);
|
||||
|
||||
user_service
|
||||
@@ -79,11 +79,12 @@ async fn password_reset_self_and_admin_flow()
|
||||
)
|
||||
.await?;
|
||||
|
||||
let revoked_tokens: i64 =
|
||||
sqlx::query_scalar("SELECT COUNT(1) FROM refresh_tokens WHERE user_id = $1 AND is_revoked = TRUE")
|
||||
.bind(user.id)
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
let revoked_tokens: i64 = sqlx::query_scalar(
|
||||
"SELECT COUNT(1) FROM refresh_tokens WHERE user_id = $1 AND is_revoked = TRUE",
|
||||
)
|
||||
.bind(user.id)
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
assert!(revoked_tokens >= 1);
|
||||
|
||||
let old_login = auth_service
|
||||
@@ -136,4 +137,3 @@ async fn password_reset_self_and_admin_flow()
|
||||
let _ = login1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use hmac::{Hmac, Mac};
|
||||
use iam_service::application::services::{AuthService, TenantService};
|
||||
use iam_service::models::{CreateTenantRequest, CreateUserRequest, LoginRequest};
|
||||
use iam_service::services::{AuthService, TenantService};
|
||||
use sha2::Sha256;
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
@@ -12,8 +12,7 @@ fn fingerprint(pepper: &str, token: &str) -> String {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn refresh_token_rotate_and_expire_cases()
|
||||
-> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn refresh_token_rotate_and_expire_cases() -> 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(()),
|
||||
@@ -104,4 +103,3 @@ async fn refresh_token_rotate_and_expire_cases()
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use iam_service::application::services::{
|
||||
AuthService, AuthorizationService, RoleService, TenantService,
|
||||
};
|
||||
use iam_service::models::{CreateRoleRequest, CreateTenantRequest, CreateUserRequest};
|
||||
use iam_service::services::{AuthService, AuthorizationService, RoleService, TenantService};
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::test]
|
||||
async fn role_permission_grant_and_wildcard_match()
|
||||
-> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn role_permission_grant_and_wildcard_match() -> 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(()),
|
||||
@@ -71,12 +72,7 @@ async fn role_permission_grant_and_wildcard_match()
|
||||
.await?;
|
||||
|
||||
role_service
|
||||
.grant_permissions_to_role(
|
||||
tenant.id,
|
||||
role.id,
|
||||
vec!["cms:*:*".to_string()],
|
||||
admin.id,
|
||||
)
|
||||
.grant_permissions_to_role(tenant.id, role.id, vec!["cms:*:*".to_string()], admin.id)
|
||||
.await?;
|
||||
|
||||
role_service
|
||||
@@ -89,4 +85,3 @@ async fn role_permission_grant_and_wildcard_match()
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use iam_service::models::CreateRoleRequest;
|
||||
use iam_service::services::{RoleService, TenantService};
|
||||
use iam_service::application::services::{RoleService, TenantService};
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user