paperclip
paperclip copied to clipboard
Support basic auth if specified in security definitions
Currently, we support adding root certs and enabling client verification, but we also need basic auth. We should support that in the generated client if the field is specified and add an option to CLI regardless.
Note that basic auth can be specified globally or locally to some particular operation.
I would like to work on this.
Cool!
So, this is basically what I had in mind.
The two traits of interest are ApiClient and Sendable.
trait ApiClient {
//
}
trait Sendable {
type Output: DeserializeOwned + Send + 'static;
// other thingies ...
fn send(&self, client: &dyn ApiClient) -> Box<...> {
//
}
}
A number of things here should be type/generic parameters, but they're coupled to
reqwest::async::*as of now (which is fine, becausereqwestdoesn't have a competition).
Sendable is implemented for all "fulfilled" builder structs i.e., builders which have the necessary parameters for making an API call. So, each impl represents an operation.
In OpenAPI, auth can be imposed on all operations or one particular operation. In order to add type-level constraint for auth, we should extend ApiClient for security stuff.
trait ApiClient<Auth> {
//
}
struct NoAuth;
By default, reqwest::async::Client (which currently impls ApiClient) will now impl ApiClient<NoAuth>. We'll have an extension trait for transforming the default client to other kinds of clients.
struct AuthBasic;
struct AuthApiKey;
trait ClientExt {
type BasicAuthClient: ApiClient<AuthBasic>;
type ApiKeyClient: ApiClient<AuthApiKey>;
// Other things (like OAuth) will follow later.
fn with_basic_auth(&self, user: &str, pass: &str) -> Self::BasicAuthClient;
fn with_api_key(&self, key: &str) -> Self::ApiKeyClient;
}
Then, we'll have wrappers for the actual client which store auth data.
struct ReqwestBasicAuth {
user: String,
pass: String,
client: reqwest::async::Client;
}
impl ApiClient<AuthBasic> for ReqwestBasicAuth {
// `make_request` will call `make_request` on the inner client
// and will set the basic auth on the emitted request object.
}
We're binding Auth as a generic to Sendable, because we can't limit Sendable to the base ApiClient (no AsRef<BaseClient>), and that's because the different kinds of clients store the auth data. But, for operations, auth data is actually needed by the RequestBuilder (or Request) structs emitted by those clients (and not the clients themselves) as shown above.
Now, the trait impl for some operation with basic auth will look like:
impl Sendable<AuthBasic> for SomeBuilder<FooExists> {
// thingies ...
}
... whereas for non-auth operations, we'll have:
impl<A> Sendable<A> for SomeBuilder<FooExists> {
// thingies ...
}
...
reqwestdoesn't have a competition.
I was wrong. Surf is here and it looks really promising with async/await. I guess it's time to rethink ApiClient and Sendable.