mirror of
https://github.com/Arcadia-Solutions/arcadia.git
synced 2025-12-20 00:39:34 -06:00
358 lines
11 KiB
Rust
358 lines
11 KiB
Rust
pub mod common;
|
|
pub mod mocks;
|
|
|
|
use actix_web::{
|
|
http::{header::HeaderValue, StatusCode},
|
|
test::{call_service, read_body, read_body_json, TestRequest},
|
|
};
|
|
use arcadia_api::services::auth::InvalidationEntry;
|
|
use arcadia_storage::{
|
|
connection_pool::ConnectionPool, models::user::RefreshToken, redis::RedisInterface,
|
|
};
|
|
use mocks::mock_redis::MockRedisPool;
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::to_string;
|
|
use sqlx::PgPool;
|
|
use std::{sync::Arc, time::Duration};
|
|
|
|
use crate::common::TestUser;
|
|
use crate::{
|
|
common::{
|
|
auth_header, call_and_read_body_json, create_test_app, create_test_app_and_login, Profile,
|
|
},
|
|
mocks::mock_redis::MockRedis,
|
|
};
|
|
|
|
#[derive(PartialEq, Debug, Serialize)]
|
|
struct RegisterRequest<'a> {
|
|
username: &'a str,
|
|
password: &'a str,
|
|
password_verify: &'a str,
|
|
email: &'a str,
|
|
}
|
|
|
|
#[derive(PartialEq, Debug, Deserialize)]
|
|
struct RegisterResponse {
|
|
username: String,
|
|
email: String,
|
|
registered_from_ip: String,
|
|
}
|
|
|
|
#[sqlx::test(migrations = "../storage/migrations")]
|
|
async fn test_open_registration(pool: PgPool) {
|
|
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
|
let service = create_test_app(pool, MockRedisPool::default()).await;
|
|
|
|
let req = TestRequest::post()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.uri("/api/auth/register")
|
|
.set_json(RegisterRequest {
|
|
username: "test_user",
|
|
password: "TestPassword123",
|
|
password_verify: "TestPassword123",
|
|
email: "test_email@testdomain.com",
|
|
})
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
|
|
assert_eq!(resp.status(), StatusCode::CREATED);
|
|
assert_eq!(
|
|
resp.headers().get("Content-Type"),
|
|
Some(&HeaderValue::from_static("application/json"))
|
|
);
|
|
|
|
let user = read_body_json::<RegisterResponse, _>(resp).await;
|
|
|
|
assert_eq!(
|
|
user,
|
|
RegisterResponse {
|
|
username: "test_user".into(),
|
|
email: "test_email@testdomain.com".into(),
|
|
// TODO: strip unnecessary /32 host postfix
|
|
registered_from_ip: "10.10.4.88/32".into(),
|
|
}
|
|
);
|
|
}
|
|
|
|
#[sqlx::test(migrations = "../storage/migrations")]
|
|
async fn test_duplicate_username_registration(pool: PgPool) {
|
|
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
|
let service = create_test_app(pool, MockRedisPool::default()).await;
|
|
|
|
// Register first user
|
|
let req = TestRequest::post()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.uri("/api/auth/register")
|
|
.set_json(RegisterRequest {
|
|
username: "duplicate_user",
|
|
password: "TestPassword123",
|
|
password_verify: "TestPassword123",
|
|
email: "test_email@testdomain.com",
|
|
})
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
assert_eq!(resp.status(), StatusCode::CREATED);
|
|
|
|
// Try to register second user with same username
|
|
let req = TestRequest::post()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.89"))
|
|
.uri("/api/auth/register")
|
|
.set_json(RegisterRequest {
|
|
username: "duplicate_user",
|
|
password: "DifferentPassword456",
|
|
password_verify: "DifferentPassword456",
|
|
email: "different_email@testdomain.com",
|
|
})
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
|
|
// Verify appropriate error response
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
|
|
// Check error message in response body
|
|
let body = read_body(resp).await;
|
|
let error: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(error["error"], "username already exists");
|
|
}
|
|
|
|
#[sqlx::test(
|
|
fixtures("with_test_users", "with_test_user_invite"),
|
|
migrations = "../storage/migrations"
|
|
)]
|
|
async fn test_closed_registration_failures(pool: PgPool) {
|
|
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
|
let service = create_test_app(pool, MockRedisPool::default()).await;
|
|
|
|
// No key specified. Should fail.
|
|
let req = TestRequest::post()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.uri("/api/auth/register")
|
|
.set_json(RegisterRequest {
|
|
username: "test_user",
|
|
password: "TestPassword123",
|
|
password_verify: "TestPassword123",
|
|
email: "test_email@testdomain.com",
|
|
})
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
|
|
// No invitation key provided when closed registration - returns BAD_REQUEST
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
assert_eq!(
|
|
resp.headers().get("Content-Type"),
|
|
Some(&HeaderValue::from_static("application/json"))
|
|
);
|
|
|
|
// Invalid key specified. Should fail.
|
|
let req = TestRequest::post()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.uri("/api/auth/register?invitation_key=invalid")
|
|
.set_json(RegisterRequest {
|
|
username: "test_user",
|
|
password: "TestPassword123",
|
|
password_verify: "TestPassword123",
|
|
email: "test_email@testdomain.com",
|
|
})
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
|
|
// Invalid invitation key - returns BAD_REQUEST
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
assert_eq!(
|
|
resp.headers().get("Content-Type"),
|
|
Some(&HeaderValue::from_static("application/json"))
|
|
);
|
|
}
|
|
|
|
#[sqlx::test(
|
|
fixtures("with_test_users", "with_test_user_invite"),
|
|
migrations = "../storage/migrations"
|
|
)]
|
|
async fn test_closed_registration_success(pool: PgPool) {
|
|
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
|
let service = create_test_app(pool, MockRedisPool::default()).await;
|
|
|
|
let req = TestRequest::post()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.uri("/api/auth/register?invitation_key=valid_key")
|
|
.set_json(RegisterRequest {
|
|
username: "test_user2",
|
|
password: "TestPassword456",
|
|
password_verify: "TestPassword456",
|
|
email: "newuser@testdomain.com",
|
|
})
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
|
|
assert_eq!(resp.status(), StatusCode::CREATED);
|
|
assert_eq!(
|
|
resp.headers().get("Content-Type"),
|
|
Some(&HeaderValue::from_static("application/json"))
|
|
);
|
|
|
|
let user = read_body_json::<RegisterResponse, _>(resp).await;
|
|
|
|
assert_eq!(
|
|
user,
|
|
RegisterResponse {
|
|
username: "test_user2".into(),
|
|
email: "newuser@testdomain.com".into(),
|
|
// TODO: strip unnecessary /32 host postfix
|
|
registered_from_ip: "10.10.4.88/32".into(),
|
|
}
|
|
);
|
|
|
|
// Try again with same key. Should fail.
|
|
let req = TestRequest::post()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.uri("/api/auth/register?invitation_key=valid_key")
|
|
.set_json(RegisterRequest {
|
|
username: "test_user3",
|
|
password: "TestPassword789",
|
|
password_verify: "TestPassword789",
|
|
email: "newuser2@testdomain.com",
|
|
})
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
|
|
// Invitation key already used - returns BAD_REQUEST
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[sqlx::test(
|
|
fixtures("with_test_users", "with_expired_test_user_invite"),
|
|
migrations = "../storage/migrations"
|
|
)]
|
|
async fn test_closed_registration_expired_failure(pool: PgPool) {
|
|
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
|
let service = create_test_app(pool, MockRedisPool::default()).await;
|
|
|
|
let req = TestRequest::post()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.uri("/api/auth/register?invitation_key=valid_key")
|
|
.set_json(RegisterRequest {
|
|
username: "test_user2",
|
|
password: "test_password2",
|
|
password_verify: "test_password2",
|
|
email: "newuser@testdomain.com",
|
|
})
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
|
|
// Expired invitation key - returns BAD_REQUEST
|
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
|
assert_eq!(
|
|
resp.headers().get("Content-Type"),
|
|
Some(&HeaderValue::from_static("application/json"))
|
|
);
|
|
}
|
|
|
|
#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")]
|
|
async fn test_authorized_endpoint_after_login(pool: PgPool) {
|
|
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
|
let (service, user) =
|
|
create_test_app_and_login(pool, MockRedisPool::default(), TestUser::Standard).await;
|
|
|
|
let req = TestRequest::get()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.insert_header(auth_header(&user.token))
|
|
.uri("/api/users/me")
|
|
.to_request();
|
|
|
|
#[derive(PartialEq, Deserialize)]
|
|
struct User {
|
|
username: String,
|
|
}
|
|
#[derive(PartialEq, Deserialize)]
|
|
struct MeResponse {
|
|
user: User,
|
|
}
|
|
|
|
let user = call_and_read_body_json::<MeResponse, _>(&service, req).await;
|
|
|
|
assert_eq!(user.user.username, "user_basic");
|
|
}
|
|
|
|
#[sqlx::test(
|
|
fixtures("with_test_banned_user"),
|
|
migrations = "../storage/migrations"
|
|
)]
|
|
async fn test_login_with_banned_user(pool: PgPool) {
|
|
let service = create_test_app(
|
|
Arc::new(ConnectionPool::with_pg_pool(pool)),
|
|
MockRedisPool::default(),
|
|
)
|
|
.await;
|
|
|
|
// Login first
|
|
let req = TestRequest::post()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.uri("/api/auth/login")
|
|
.set_json(serde_json::json!({
|
|
"username": "test_user",
|
|
"password": "test_password",
|
|
"remember_me": true,
|
|
}))
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")]
|
|
async fn test_refresh_with_invalidated_token(pool: PgPool) {
|
|
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
|
let (service, user) = create_test_app_and_login(
|
|
Arc::clone(&pool),
|
|
MockRedisPool::default(),
|
|
TestUser::Standard,
|
|
)
|
|
.await;
|
|
|
|
// invalidate user tokens
|
|
let req = TestRequest::get()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.uri("/api/users/me")
|
|
.insert_header(("authorization", format!("Bearer {}", user.token.clone())))
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
let profile = read_body_json::<Profile, _>(resp).await;
|
|
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
let mut redis_conn = MockRedis::default();
|
|
let entry = InvalidationEntry::new(profile.user.id);
|
|
redis_conn
|
|
.set(profile.user.id.to_string(), to_string(&entry).unwrap())
|
|
.await
|
|
.unwrap();
|
|
|
|
let (service, _) = create_test_app_and_login(
|
|
Arc::clone(&pool),
|
|
MockRedisPool::with_conn(redis_conn),
|
|
TestUser::Standard,
|
|
)
|
|
.await;
|
|
|
|
let payload = RefreshToken {
|
|
refresh_token: user.refresh_token.clone(),
|
|
};
|
|
let req = TestRequest::post()
|
|
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
|
.uri("/api/auth/refresh-token")
|
|
.set_json(payload)
|
|
.to_request();
|
|
|
|
let resp = call_service(&service, req).await;
|
|
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
|
|
}
|