use async_trait::async_trait;
use reqwest::{Client, RequestBuilder, Url};
use static_assertions::assert_impl_all;
use super::internal::Internal;
use super::protocol;
use super::Scope;
use crate::common::IdOrName;
use crate::{AuthType, EndpointFilters, Error};
#[derive(Debug, Clone)]
pub struct Password {
inner: Internal,
}
assert_impl_all!(Password: Send, Sync);
impl Password {
pub fn new<U, S1, S2, S3>(
auth_url: U,
user_name: S1,
password: S2,
user_domain_name: S3,
) -> Result<Password, Error>
where
U: AsRef<str>,
S1: Into<String>,
S2: Into<String>,
S3: Into<String>,
{
let pw = protocol::UserAndPassword {
user: IdOrName::Name(user_name.into()),
password: password.into(),
domain: Some(IdOrName::Name(user_domain_name.into())),
};
let body = protocol::AuthRoot {
auth: protocol::Auth {
identity: protocol::Identity::Password(pw),
scope: None,
},
};
Ok(Password {
inner: Internal::new(auth_url.as_ref(), body)?,
})
}
#[inline]
pub fn set_project_scope(&mut self, project: IdOrName, domain: impl Into<Option<IdOrName>>) {
self.set_scope(Scope::Project {
project,
domain: domain.into(),
});
}
#[inline]
pub fn set_scope(&mut self, scope: Scope) {
self.inner.set_scope(scope);
}
#[inline]
pub fn with_project_scope(
mut self,
project: IdOrName,
domain: impl Into<Option<IdOrName>>,
) -> Password {
self.set_project_scope(project, domain);
self
}
#[inline]
pub fn with_scope(mut self, scope: Scope) -> Self {
self.set_scope(scope);
self
}
#[inline]
pub fn user(&self) -> &IdOrName {
self.inner.user().expect("Password auth without a user")
}
#[inline]
pub fn project(&self) -> Option<&IdOrName> {
self.inner.project()
}
}
#[async_trait]
impl AuthType for Password {
async fn authenticate(
&self,
client: &Client,
request: RequestBuilder,
) -> Result<RequestBuilder, Error> {
self.inner.authenticate(client, request).await
}
async fn get_endpoint(
&self,
client: &Client,
service_type: &str,
filters: &EndpointFilters,
) -> Result<Url, Error> {
self.inner.get_endpoint(client, service_type, filters).await
}
async fn refresh(&self, client: &Client) -> Result<(), Error> {
self.inner.refresh(client, true).await
}
}
#[cfg(test)]
pub mod test {
#![allow(unused_results)]
use reqwest::Url;
use super::Password;
use crate::identity::IdOrName;
#[test]
fn test_identity_new() {
let id = Password::new("http://127.0.0.1:8080/", "admin", "pa$$w0rd", "Default").unwrap();
let e = Url::parse(id.inner.token_endpoint()).unwrap();
assert_eq!(e.scheme(), "http");
assert_eq!(e.host_str().unwrap(), "127.0.0.1");
assert_eq!(e.port().unwrap(), 8080u16);
assert_eq!(e.path(), "/v3/auth/tokens");
assert_eq!(id.user(), &IdOrName::Name("admin".to_string()));
}
#[test]
fn test_identity_new_invalid() {
Password::new("http://127.0.0.1 8080/", "admin", "pa$$w0rd", "Default")
.err()
.unwrap();
}
#[test]
fn test_identity_create() {
let id = Password::new(
"http://127.0.0.1:8080/identity",
"user",
"pa$$w0rd",
"example.com",
)
.unwrap()
.with_project_scope(
IdOrName::Name("cool project".to_string()),
IdOrName::Name("example.com".to_string()),
);
assert_eq!(id.user(), &IdOrName::Name("user".to_string()));
assert_eq!(
id.project(),
Some(&IdOrName::Name("cool project".to_string()))
);
assert_eq!(
id.inner.token_endpoint(),
"http://127.0.0.1:8080/identity/v3/auth/tokens"
);
}
#[test]
fn test_token_endpoint_with_trailing_slash() {
let id = Password::new(
"http://127.0.0.1:8080/identity/",
"user",
"pa$$w0rd",
"example.com",
)
.unwrap()
.with_project_scope(
IdOrName::Name("cool project".to_string()),
IdOrName::Name("example.com".to_string()),
);
assert_eq!(id.user(), &IdOrName::Name("user".to_string()));
assert_eq!(
id.project(),
Some(&IdOrName::Name("cool project".to_string()))
);
assert_eq!(
id.inner.token_endpoint(),
"http://127.0.0.1:8080/identity/v3/auth/tokens"
);
}
#[test]
fn test_token_endpoint_with_v3() {
let id = Password::new(
"http://127.0.0.1:8080/identity/v3",
"user",
"pa$$w0rd",
"example.com",
)
.unwrap()
.with_project_scope(
IdOrName::Name("cool project".to_string()),
IdOrName::Name("example.com".to_string()),
);
assert_eq!(id.user(), &IdOrName::Name("user".to_string()));
assert_eq!(
id.project(),
Some(&IdOrName::Name("cool project".to_string()))
);
assert_eq!(
id.inner.token_endpoint(),
"http://127.0.0.1:8080/identity/v3/auth/tokens"
);
}
#[test]
fn test_token_endpoint_with_trailing_slash_v3() {
let id = Password::new(
"http://127.0.0.1:8080/identity/v3/",
"user",
"pa$$w0rd",
"example.com",
)
.unwrap()
.with_project_scope(
IdOrName::Name("cool project".to_string()),
IdOrName::Name("example.com".to_string()),
);
assert_eq!(id.user(), &IdOrName::Name("user".to_string()));
assert_eq!(
id.project(),
Some(&IdOrName::Name("cool project".to_string()))
);
assert_eq!(
id.inner.token_endpoint(),
"http://127.0.0.1:8080/identity/v3/auth/tokens"
);
}
#[test]
fn test_token_endpoint_root() {
let id = Password::new("http://127.0.0.1:8080", "user", "pa$$w0rd", "example.com")
.unwrap()
.with_project_scope(
IdOrName::Name("cool project".to_string()),
IdOrName::Name("example.com".to_string()),
);
assert_eq!(id.user(), &IdOrName::Name("user".to_string()));
assert_eq!(
id.project(),
Some(&IdOrName::Name("cool project".to_string()))
);
assert_eq!(
id.inner.token_endpoint(),
"http://127.0.0.1:8080/v3/auth/tokens"
);
}
}