Closed aldbr closed 1 year ago
I think the general approach looks good and like the right way to go 👍
I'll do a specific review once you figure out the CI.
mypy
was tough but probably fair.
Here are the changes I performed to make mypy
happy:
src/diracx/cli/__init__.py:32: error: "Dirac" has no attribute "login"
Dirac
client as DiracClient
Dirac().__aenter__()
in DiracClient()
src/diracx/client/aio/_patch.py:51: error: Signature of "get_token" incompatible with supertype "AsyncTokenCredential" [override]
AsyncTokenCredential().close()/__aenter__()/__aexit__()
in DiracAsyncTokenCredential()
DiracAsyncTokenCredential().__aexit__()
with ...
as default valuesIt is just for info in case there are better solutions.
The issue related to DIRAC Integration tests is expected, I am going to make a PR once we are okay with the structure of this one.
Update: mypy
now complains about the redefinition of the DiracBearerTokenCredentialPolicy()._token()
but I don't see how I could do this differently since I can't modify self._token.expires_on
which a read-only attribute.
The issue with the integration tests is related to the normal (non-aio) Dirac
client, which is used by DIRAC, and which is actually not implemented here.
The easiest option would be to almost duplicate the content of the aio Dirac
client in diracx/client/_patch.py
.
I can see if there is an easy way to avoid duplicating too much code, I will work on that asap.
To get the CI green again I've pushed a workaround to main
, this commit should be reverted: 2c70a6787
I'm adding a comment to explain the latest changes because it is becoming hard to review and I am sorry for that:
DiracClient
version in src/diracx/client/_patch.py
. I created a few methods to refactor codes used by both sync/async clients.state
parameter from the TokenResponse
pydantic model because it is not part of the OAuth2 RFC and it is currently not used. I had to regenerate the client to make mypy happy but it generated a large number of changes in the client files.
This PR aims to enhance the
autorest
Dirac
client by encapsulating the configuration options such as the authorization headers. The primary purpose is to simplify code for thecli
developers.writing a new cli command now:
with this PR:
Note: the
Dirac
client is able to auto refresh the access token.Chosen solution
The solution is based on the
azure.core
architecture. We redefine 3 classes:Dirac
: to encapsulate the endpoint and an authentication policyAsyncTokenCredential
: provide OAuth tokensAsyncBearerTokenCredentialPolicy
: an authentication policy which aims at adding the authorization headers to the requestsThis solution was chosen after thorough research and analysis, as it was deemed (by myself) the simplest way to incorporate authorization headers into requests.
Considered alternatives
azure-identity
The python autorest documentation recommends using a credential type from the
azure identity
package to initialize the client.The DeviceCodeCredentials seems adapted to our use case. It is able to obtain a token using the
device_code
flow and cache it. Then it is automatically refreshed when needed.Problem: it is tightly coupled with Azure applications.
DeviceCodeCredential
inherits fromInteractiveCredential
, which inherits fromMsalCredential
. There are mandatory non-standard parameters such astenant_id
. Setting it to""
orNone
does not help. The only option would have been to create new classes inheriting from the mentioned classes, which would have been inefficient.AzureAD
authentication librarymicrosoft-authentication-library-for-python seems to provide a generic OAuth2 library but does not automatically refresh access tokens. Furthermore, it would probably be tricky to integrate to the
Dirac
autorest client.authlib
in combination withazure.core
authlib
would help managing tokens. It is even able to automatically refresh access tokens. But the library would be limited in our context as theDirac
client is initialized each time a new command starts.