Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete Project Get Endpoint #61

Merged
merged 8 commits into from
Sep 29, 2024
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ This is the combined changelog of all contained `lrzcc` crates.
- api: bump thiserror from 1.0.63 to 1.0.64
- wire: remove ProjectCreated
- lib/api: refactor to use Project for project create response
- wire: add ProjectListParams
- lib: use wire.project.ProjectListParams in project list with serde_urlencoded
- api: implement all and user class filters for project list
- test: revise project list tests to use all parameter
- test: split project tests into submodules and add more tests

## [lrzcc-cli-v1.1.2] - 2024-09-24

Expand Down
6 changes: 3 additions & 3 deletions api/src/routes/user/project/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ use anyhow::Context;
use lrzcc_wire::user::{Project, ProjectDetailed, User};
use sqlx::{Executor, FromRow, MySql, MySqlPool, Transaction};

// TODO proper query set and permissions
#[tracing::instrument(name = "project_get")]
pub async fn project_get(
user: ReqData<User>,
// TODO: we don't need this right?
project: ReqData<Project>,
db_pool: Data<MySqlPool>,
params: Path<ProjectIdParam>,
) -> Result<HttpResponse, OptionApiError> {
require_admin_user(&user)?;
if params.project_id != project.id {
require_admin_user(&user)?;
}
let mut transaction = db_pool
.begin()
.await
Expand Down
1 change: 0 additions & 1 deletion api/src/routes/user/project/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use sqlx::{Executor, FromRow, MySql, MySqlPool, Transaction};
#[tracing::instrument(name = "project_list")]
pub async fn project_list(
user: ReqData<User>,
// TODO: we don't need this right?
project: ReqData<Project>,
db_pool: Data<MySqlPool>,
params: Query<ProjectListParams>,
Expand Down
2 changes: 2 additions & 0 deletions api/src/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ impl Application {
let port = listener.local_addr().unwrap().port();
let openstack = OpenStack::new(configuration.openstack).await?;

// TODO: insert admin user and project into database

let server = run(
listener,
connection_pool,
Expand Down
96 changes: 93 additions & 3 deletions test/tests/user/project/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::str::FromStr;
use tokio::task::spawn_blocking;

#[tokio::test]
async fn e2e_lib_project_get_denies_access_to_normal_user() {
async fn e2e_lib_user_can_get_own_project() {
// arrange
let server = spawn_app().await;
let (user, project, token) = server
Expand All @@ -28,7 +28,51 @@ async fn e2e_lib_project_get_denies_access_to_normal_user() {
.unwrap();

// act
let get = client.project.get(project.id);
let ProjectRetrieved::Detailed(project_detailed) =
client.project.get(project.id).unwrap()
else {
panic!("Expected ProjectDetailed")
};

// assert
assert_eq!(project.id, project_detailed.id);
assert_eq!(project.name, project_detailed.name);
assert_eq!(project.openstack_id, project_detailed.openstack_id);
assert_eq!(project.user_class, project_detailed.user_class);
})
.await
.unwrap();
}

#[tokio::test]
async fn e2e_lib_user_cannot_get_other_project() {
// arrange
let server = spawn_app().await;
let (user, _project, token) = server
.setup_test_user_and_project(false)
.await
.expect("Failed to setup test user and project.");
let (_user2, project2, _token2) = server
.setup_test_user_and_project(false)
.await
.expect("Failed to setup test user2 and project.");
server
.mock_keystone_auth(&token, &user.openstack_id, &user.name)
.mount(&server.keystone_server)
.await;

spawn_blocking(move || {
// arrange
let client = Api::new(
format!("{}/api", &server.address),
Token::from_str(&token).unwrap(),
None,
None,
)
.unwrap();

// act
let get = client.project.get(project2.id);

// assert
assert!(get.is_err());
Expand All @@ -42,7 +86,7 @@ async fn e2e_lib_project_get_denies_access_to_normal_user() {
}

#[tokio::test]
async fn e2e_lib_project_get_for_own_project_of_admin() {
async fn e2e_lib_admin_can_get_own_project() {
// arrange
let server = spawn_app().await;
let (user, project, token) = server
Expand Down Expand Up @@ -80,3 +124,49 @@ async fn e2e_lib_project_get_for_own_project_of_admin() {
.await
.unwrap();
}

#[tokio::test]
async fn e2e_lib_admin_can_get_other_project() {
// arrange
let server = spawn_app().await;
let (user, _project, token) = server
.setup_test_user_and_project(true)
.await
.expect("Failed to setup test user and project.");
let (_user2, project2, _token2) = server
.setup_test_user_and_project(false)
.await
.expect("Failed to setup test user2 and project.");
server
.mock_keystone_auth(&token, &user.openstack_id, &user.name)
.mount(&server.keystone_server)
.await;

spawn_blocking(move || {
// arrange
let client = Api::new(
format!("{}/api", &server.address),
Token::from_str(&token).unwrap(),
None,
None,
)
.unwrap();

// act
let ProjectRetrieved::Detailed(project_detailed) =
client.project.get(project2.id).unwrap()
else {
panic!("Expected ProjectDetailed")
};

// assert
assert_eq!(project2.id, project_detailed.id);
assert_eq!(project2.name, project_detailed.name);
assert_eq!(project2.openstack_id, project_detailed.openstack_id);
assert_eq!(project2.user_class, project_detailed.user_class);
})
.await
.unwrap();
}

// TODO: add master user cases
Loading