Repository navigation
feat: implement management client with auth module - #49
shefali-kamal wants to merge 1 commit into
Conversation
shefali-kamal
commented
Sep 23, 2026
- implement management client for veraison/services
- add auth module required to submit the policy to the management server
paulhowardarm
left a comment
There was a problem hiding this comment.
Nice extra function for the Rust client, thanks. Looks good overall, although I'm not too familiar with the management API, so definitely worth a second review. I have requested some naming changes, and offered some advice on zeroing sensitive data fields in the authentication parts.
|
|
||
| /// Supplies the value of the HTTP Authorization header. | ||
| #[async_trait::async_trait] | ||
| pub trait IAuthenticator { |
There was a problem hiding this comment.
The name IAuthenticator is not idiomatic for Rust traits. At the very least, the I prefix should be removed, because this is not a convention in Rust. Authenticator is probably suitable, but an alternative would be Authenticate. Rust tends to use verbs or agent nouns for traits.
|
|
||
| /// A Basic authenticator that uses a username and password to generate a Basic Authorization header. | ||
| #[derive(Default, Deserialize)] | ||
| pub struct BasicAuthenticator { |
There was a problem hiding this comment.
Since this type contains a secret (password), it would probably be good practice to zeroize it on drop. Look at using the zeroize crate and provide a Drop implementation. See: https://docs.rs/zeroize/latest/zeroize/
| } | ||
|
|
||
| /// An OAuth2 authenticator that uses the Resource Owner Password Credentials grant to obtain an access token and generate a Bearer Authorization header. | ||
| #[derive(Default, Deserialize)] |
There was a problem hiding this comment.
As with BasicAuthenticator, we probably want to ensure any secret-containing fields are zeroized on drop.
| @@ -90,6 +134,7 @@ pub trait ConfigureHttp: Sized { | |||
| /// The common builder allows for the configuration of custom TLS root certificates along with | |||
| /// middleware layers for client-side caching. | |||
There was a problem hiding this comment.
Update the comment here - middleware layers for client-side caching or authentication.
| let authentication = AuthenticationMiddleware { | ||
| authenticator: Arc::new(Mutex::new(self.authenticator)), | ||
| }; | ||
| // Now add any required middleware to the client |
There was a problem hiding this comment.
Need to reposition the comment about adding middleware, because we've already started doing so.
| } | ||
|
|
||
| /// A builder for [PolicyRunner] objects. | ||
| pub struct PolicyRunnerBuilder { |
There was a problem hiding this comment.
I would reconsider the names PolicyRunner and PolicyRunnerBuilder. I don't think Runner is very suitable in this context. I suspect this might have been borrowed from the CoSERV client, which uses QueryRunner. But it works in that case because the CoSERV client is about running queries. I think PolicyManager and PolicyManagerBuilder would make more sense than Runner here.
- implement management client for veraison/services - add auth module required to submit the policy to the management server Signed-off-by: Badiyasar, Harshita <Harshita.Badiyasar@fujitsu.com>
|
Hi @paulhowardarm , |
paulhowardarm
left a comment
There was a problem hiding this comment.
Thank you for addressing my earlier observations. Sorry for the delay on re-review, but this LGTM now.
|
Hi @thomas-fossati , |
thomas-fossati
left a comment
There was a problem hiding this comment.
Thanks for the nice contribution and sorry for taking so long to review.
I have inlined a few comments.
| ) -> MiddlewareResult<reqwest::Response> { | ||
| let header = self | ||
| .authenticator | ||
| .lock() |
There was a problem hiding this comment.
this is good, because it avoids n parallel refreshes, but if the IdP is slow or hung up this will block indefinitely. We should probably add a "reasonable" timeout to the token request.
| .append_pair("scope", "openid") | ||
| .finish(); | ||
| let response = client | ||
| .post(&self.token_url) |
There was a problem hiding this comment.
(see comment in http.rs about "locking forever".)
we should put a timeout on this request, say 15-20 seconds.
| { | ||
| let mut builder = HttpClientBuilder::new(); | ||
| for path in cert_paths { | ||
| builder = builder.with_root_certificate(PathBuf::from(path.as_ref())); |
There was a problem hiding this comment.
hold on, with_root_certificate is not additive, right? so, if len(cert_paths) > 1 only the last cert survives, which is probably not what you intended.
| let mut url = self.base_url.clone(); | ||
| let path = template.replace(":scheme", scheme).replace( | ||
| ":uuid", | ||
| &policy_id.map_or_else(String::new, |id| id.to_string()), | ||
| ); | ||
| url.set_path(&path); |
There was a problem hiding this comment.
this throws away the base URL's path, which may be problematic if the service is behind a reverse proxy/LB that mounts the service on a dedicated path.
another thought: who selects scheme? Asking because applying set_path on an arbitrary input may result in "path evasion" situations.
|
|
||
| impl CachedTokenResponse { | ||
| // If expiry is not given then do not cache the token, and always obtain a new one. | ||
| fn is_expired(&self) -> bool { |
There was a problem hiding this comment.
this can become a bit wobbly when the token is nearly but not quite yet expired: it'll pass the check but then it'll get rejected.
I suggest to subtract a few seconds of skew (say 30s).
A (slightly more convoluted) alternative would be to retry once on 401.
| } | ||
|
|
||
| /// Get all policies for a scheme. (optially filtered by name) | ||
| pub async fn get_policies(&self, scheme: &str, name: &str) -> Result<Vec<Policy>, Error> { |
There was a problem hiding this comment.
maybe name: Option<&str>
| Self::policy_from_response(Self::expect_status(response, StatusCode::OK).await?).await | ||
| } | ||
|
|
||
| /// Get all policies for a scheme. (optially filtered by name) |
There was a problem hiding this comment.
| /// Get all policies for a scheme. (optially filtered by name) | |
| /// Get all policies for a scheme. (optionally filtered by name) |
| .append_pair("grant_type", "password") | ||
| .append_pair("username", &self.username) | ||
| .append_pair("password", &self.password) | ||
| .append_pair("scope", "openid") |
There was a problem hiding this comment.
why is this hardcoded? I think that the scope should be configurable.
| } | ||
| } | ||
|
|
||
| /// Obtains the capabilities and endpoints of the Veraison verification service. |
There was a problem hiding this comment.
| /// Obtains the capabilities and endpoints of the Veraison verification service. | |
| /// Obtains the capabilities and endpoints of the Veraison management service. |
| const CREATE_POLICY_ENDPOINT: &str = "/management/v1/policy/:scheme"; | ||
| const GET_POLICY_ENDPOINT: &str = "/management/v1/policy/:scheme/:uuid"; | ||
| const DEACTIVATE_POLICIES_ENDPOINT: &str = "/management/v1/policies/:scheme/deactivate"; | ||
| const GET_ACTIVE_POLICY_ENDPOINT: &str = "/management/v1/policy/:scheme"; | ||
| const ACTIVATE_POLICY_ENDPOINT: &str = "/management/v1/policy/:scheme/:uuid/activate"; | ||
| const GET_POLICIES_ENDPOINT: &str = "/management/v1/policies/:scheme"; |
There was a problem hiding this comment.
unclear if these are hardcoded or discovered?