diff --git a/.github/workflows/functional.yml b/.github/workflows/functional.yml index b634908d..7b8c150b 100644 --- a/.github/workflows/functional.yml +++ b/.github/workflows/functional.yml @@ -104,6 +104,9 @@ jobs: curl http://localhost:8080/v3/auth/tokens -H "X-Auth-Token: ${TOKEN2}" -H "X-Subject-Token: ${TOKEN2}" | jq curl http://localhost:5001/v3/auth/tokens -H "X-Auth-Token: ${TOKEN2}" -H "X-Subject-Token: ${TOKEN2}" | jq + - name: Run functional tests + run: cargo test --test functional + - name: Run interop tests run: cargo test --test interop diff --git a/Cargo.toml b/Cargo.toml index 33442e68..a3db0e59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,3 +107,9 @@ test = false name = "github" path = "tests/github/main.rs" test = false + + +[[test]] +name = "functional" +path = "tests/functional/main.rs" +test = false diff --git a/src/api/types.rs b/src/api/types.rs index 3d38a406..ccec68d8 100644 --- a/src/api/types.rs +++ b/src/api/types.rs @@ -141,7 +141,9 @@ pub struct Endpoint { pub id: String, pub url: String, pub interface: String, + #[builder(default)] pub region: Option, + #[builder(default)] pub region_id: Option, } @@ -189,18 +191,22 @@ pub enum Scope { /// Project scope information. #[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)] +#[builder(setter(into, strip_option))] pub struct ProjectScope { /// Project ID. + #[builder(default)] pub id: Option, /// Project Name. + #[builder(default)] pub name: Option, - /// project domain. + /// Project domain. + #[builder(default)] pub domain: Option, } /// Domain information. #[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)] -#[builder(setter(into))] +#[builder(setter(into, strip_option))] pub struct Domain { /// Domain ID. #[builder(default)] @@ -223,7 +229,7 @@ pub struct Project { /// System scope. #[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)] -#[builder(setter(into))] +#[builder(setter(into, strip_option))] pub struct System { /// All systems access. #[builder(default)] diff --git a/src/api/v3/auth/token/common.rs b/src/api/v3/auth/token/common.rs index 4bef7d47..7df37c21 100644 --- a/src/api/v3/auth/token/common.rs +++ b/src/api/v3/auth/token/common.rs @@ -57,7 +57,9 @@ impl Token { let mut user_response: UserBuilder = UserBuilder::default(); user_response.id(user.id.clone()); user_response.name(user.name.clone()); - user_response.password_expires_at(user.password_expires_at); + if let Some(val) = user.password_expires_at { + user_response.password_expires_at(val); + } user_response.domain(user_domain.clone()); response.user(user_response.build().map_err(TokenError::from)?); diff --git a/src/api/v3/auth/token/types.rs b/src/api/v3/auth/token/types.rs index 5023285d..535326e1 100644 --- a/src/api/v3/auth/token/types.rs +++ b/src/api/v3/auth/token/types.rs @@ -118,15 +118,18 @@ pub struct AuthRequestInner { } /// An identity object. -#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)] +#[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)] +#[builder(setter(into, strip_option))] pub struct Identity { /// The authentication method. For password authentication, specify password. pub methods: Vec, /// The password object, contains the authentication information. + #[builder(default)] pub password: Option, /// The token object, contains the authentication information. + #[builder(default)] pub token: Option, } @@ -140,13 +143,17 @@ pub struct PasswordAuth { } /// User password information -#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)] +#[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)] +#[builder(setter(into, strip_option))] pub struct UserPassword { /// User ID + #[builder(default)] pub id: Option, /// User Name + #[builder(default)] pub name: Option, /// User domain + #[builder(default)] pub domain: Option, /// User password expiry date pub password: String, @@ -180,7 +187,7 @@ impl TryFrom for identity_types::UserPasswordAuthRequest { /// User information #[derive(Builder, Clone, Debug, Default, Deserialize, PartialEq, Serialize, ToSchema)] -#[builder(setter(into))] +#[builder(setter(into, strip_option))] pub struct User { /// User ID pub id: String, diff --git a/tests/functional/auth.rs b/tests/functional/auth.rs new file mode 100644 index 00000000..58f35a00 --- /dev/null +++ b/tests/functional/auth.rs @@ -0,0 +1,15 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +mod token; diff --git a/tests/functional/auth/token.rs b/tests/functional/auth/token.rs new file mode 100644 index 00000000..11354d61 --- /dev/null +++ b/tests/functional/auth/token.rs @@ -0,0 +1,15 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +mod validate; diff --git a/tests/functional/auth/token/validate.rs b/tests/functional/auth/token/validate.rs new file mode 100644 index 00000000..ffea1028 --- /dev/null +++ b/tests/functional/auth/token/validate.rs @@ -0,0 +1,53 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +use reqwest::Client; +use std::env; + +use openstack_keystone::api::types::*; +use openstack_keystone::api::v3::auth::token::types::*; + +use crate::common::*; + +#[tokio::test] +async fn test_validate_own() { + let keystone_url = env::var("KEYSTONE_URL").expect("KEYSTONE_URL is set"); + let client = Client::new(); + + let token = auth( + &keystone_url, + get_password_auth("admin", "password", "default").expect("can't prepare password auth"), + Some(Scope::Project( + ProjectScopeBuilder::default() + .name("admin") + .domain(DomainBuilder::default().id("default").build().unwrap()) + .build() + .unwrap(), + )), + ) + .await + .expect("no token"); + + let auth_rsp: TokenResponse = client + .get(format!("{}/v3/auth/tokens", keystone_url)) + .header("x-auth-token", token.clone()) + .header("x-subject-token", token.clone()) + .send() + .await + .unwrap() + .json() + .await + .unwrap(); + println!("Token: {:?}", auth_rsp); +} diff --git a/tests/functional/common.rs b/tests/functional/common.rs new file mode 100644 index 00000000..c60af87e --- /dev/null +++ b/tests/functional/common.rs @@ -0,0 +1,74 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +//! Common functionality used in the functional tests. + +use eyre::Report; +use reqwest::Client; + +use openstack_keystone::api::types::*; +use openstack_keystone::api::v3::auth::token::types::*; + +/// Get the password auth identity struct +pub fn get_password_auth( + username: U, + password: P, + domain_id: DID, +) -> Result +where + U: AsRef, + P: AsRef, + DID: AsRef, +{ + PasswordAuthBuilder::default() + .user( + UserPasswordBuilder::default() + .name(username.as_ref()) + .password(password.as_ref()) + .domain(DomainBuilder::default().id(domain_id.as_ref()).build()?) + .build()?, + ) + .build() + .map_err(Into::into) +} + +/// Authenticate using the passed password auth and the scope. +pub async fn auth( + keystone_url: U, + password_auth: PasswordAuth, + scope: Option, +) -> Result +where + U: AsRef + std::fmt::Display, +{ + let identity = IdentityBuilder::default() + .methods(vec!["password".into()]) + .password(password_auth) + .build()?; + let auth_request = AuthRequest { + auth: AuthRequestInner { identity, scope }, + }; + let client = Client::new(); + Ok(client + .post(format!("{}/v3/auth/tokens", keystone_url,)) + .json(&serde_json::to_value(auth_request)?) + .send() + .await + .unwrap() + .headers() + .get("X-Subject-Token") + .unwrap() + .to_str() + .unwrap() + .to_string()) +} diff --git a/tests/functional/main.rs b/tests/functional/main.rs new file mode 100644 index 00000000..51b42ce9 --- /dev/null +++ b/tests/functional/main.rs @@ -0,0 +1,16 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +mod auth; +mod common;