boundary_api/
lib.rs

1use reqwest::Client;
2use std::collections::HashMap;
3use thiserror::Error;
4use url::Url;
5
6/// BoundaryClient is used to connect to the Boundary server
7/// The Default trait is implemented to cover the Boundary server in dev mode
8/// Otherwise you can configure the client prior to authentication using builder pattern methods
9#[derive(Debug, Clone)]
10pub struct BoundaryClient {
11    pub host: Url,
12    pub auth_method_id: String,
13    pub login_name: String,
14    pub password: String,
15    client: Client,
16}
17
18impl Default for BoundaryClient {
19    /// These default values correspond to the defaults returned by running Boundary in dev mode
20    fn default() -> Self {
21        BoundaryClient::new("http://127.0.0.1:9200")
22            .expect("Provided Boundary host url failed to parse")
23            .auth_method_id("ampw_1234567890")
24            .login_name("admin")
25            .password("password")
26    }
27}
28
29impl BoundaryClient {
30    /// `new()` takes in any String or &str that returns Ok() from `Url::parse()`
31    /// Need to configure other struct values before running `authenticate()` against Boundary server
32    pub fn new<S>(host: S) -> Result<Self, BoundaryError>
33    where
34        S: AsRef<str>,
35    {
36        Ok(BoundaryClient {
37            host: Url::parse(host.as_ref())?,
38            auth_method_id: String::new(),
39            login_name: String::new(),
40            password: String::new(),
41            client: Client::new(),
42        })
43    }
44
45    /// `auth_method_id` configures the value passed to the Boundary auth method service
46    pub fn auth_method_id<'a, S>(&'a mut self, id: S) -> Self
47    where
48        S: Into<String>,
49    {
50        self.auth_method_id = id.into();
51        self.to_owned()
52    }
53
54    /// `login_name` configures the login name used for connecting to the Boundary auth method service
55    pub fn login_name<'a, S>(&'a mut self, login_name: S) -> Self
56    where
57        S: Into<String>,
58    {
59        self.login_name = login_name.into();
60        self.to_owned()
61    }
62
63    /// `password` configures the password used for connecting to the Boundary auth method service
64    pub fn password<'a, S>(&'a mut self, password: S) -> Self
65    where
66        S: Into<String>,
67    {
68        self.password = password.into();
69        self.to_owned()
70    }
71
72    /// `authenticate` will connect to the Boundary server using the current struct values
73    pub async fn authenticate<'a>(&'a mut self) -> Result<reqwest::Response, BoundaryError> {
74        let mut creds = HashMap::new();
75        creds.insert("login_name", self.login_name.as_str());
76        creds.insert("password", self.password.as_str());
77
78        let mut auth_payload = HashMap::new();
79        auth_payload.insert("credentials", creds);
80
81        let auth_endpoint = format!(
82            "/v1/auth-methods/{auth_method_id}:authenticate",
83            auth_method_id = self.auth_method_id
84        );
85        let auth_method_service = self.host.join(&auth_endpoint)?;
86
87        let req = self
88            .client
89            .post(auth_method_service)
90            .json(&auth_payload)
91            .build()?;
92
93        let res = self.client.execute(req).await?;
94
95        Ok(res)
96    }
97}
98
99/// Internal error type managed by `thiserror` crate
100#[derive(Error, Debug)]
101pub enum BoundaryError {
102    #[error(transparent)]
103    InvalidHost(#[from] url::ParseError),
104    #[error(transparent)]
105    RequestError(#[from] reqwest::Error),
106}