-
Notifications
You must be signed in to change notification settings - Fork 3.2k
address review feedback #21109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
address review feedback #21109
Changes from 4 commits
ad57965
7afd716
b7de7bc
067adc1
0f8a317
dea2f29
e670b39
34cd5d9
da98070
c712de7
ca86cf4
4388cf6
1f50449
8806099
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,42 +33,51 @@ class OnBehalfOfCredential(MsalCredential, GetTokenMixin): | |
|
|
||
| :param str tenant_id: ID of the service principal's tenant. Also called its "directory" ID. | ||
| :param str client_id: the service principal's client ID | ||
| :param client_credential: a credential to authenticate the service principal, either one of its client secrets (a | ||
| string) or the bytes of a certificate in PEM or PKCS12 format including the private key | ||
| :type client_credential: str or bytes | ||
| :param str user_assertion: the access token the credential will use as the user assertion when requesting | ||
| on-behalf-of tokens | ||
| :keyword str client_secret: Optional. client secrets to authenticate the service principal. | ||
| Either client_secret or client_certificate must be provided. | ||
| :keyword bytes client_certificate: Optional. the bytes of a certificate in PEM or PKCS12 format including | ||
| the private key to authenticate the service principal. Either client_secret or client_certificate must | ||
| be provided. | ||
| :keyword str user_assertion: Required. the access token the credential will use as the user assertion when | ||
xiangyan99 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
xiangyan99 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| requesting on-behalf-of tokens | ||
|
|
||
| :keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com", | ||
| the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts` | ||
| defines authorities for other clouds. | ||
| :keyword password: a certificate password. Used only when **client_credential** is certificate bytes. If this value | ||
| :keyword password: a certificate password. Used only when **client_certificate** is provided. If this value | ||
| is a unicode string, it will be encoded as UTF-8. If the certificate requires a different encoding, pass | ||
| appropriately encoded bytes instead. | ||
| :paramtype password: str or bytes | ||
| """ | ||
|
|
||
| def __init__(self, tenant_id, client_id, client_credential, user_assertion, **kwargs): | ||
| # type: (str, str, Union[bytes, str], str, **Any) -> None | ||
| credential = cast("Union[Dict, str]", client_credential) | ||
| if isinstance(client_credential, six.binary_type): | ||
| def __init__(self, tenant_id, client_id, **kwargs): | ||
| # type: (str, str, **Any) -> None | ||
| self._assertion = kwargs.pop("user_assertion", None) | ||
| if not self._assertion: | ||
| raise ValueError("'user_assertion' is required.") | ||
xiangyan99 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| client_certificate = kwargs.pop("client_certificate", None) | ||
| client_secret = kwargs.pop("client_secret", None) | ||
|
|
||
| if client_certificate: | ||
| try: | ||
| credential = get_client_credential( | ||
| certificate_path=None, password=kwargs.pop("password", None), certificate_data=client_credential | ||
| certificate_path=None, password=kwargs.pop("password", None), certificate_data=client_certificate | ||
| ) | ||
| except ValueError as ex: | ||
| # client_credential isn't a valid cert. On 2.7 str == bytes and we ignore this exception because we | ||
| # can't tell whether the caller intended to provide a cert. On Python 3 we can say the caller provided | ||
| # either an invalid cert, or a client secret as bytes; both are errors. | ||
| if six.PY3: | ||
| message = ( | ||
| '"client_credential" should be either a client secret (a string)' | ||
| + " or the bytes of a certificate in PEM or PKCS12 format" | ||
| '"client_certificate" should be the bytes of a certificate in PEM or PKCS12 format' | ||
xiangyan99 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ) | ||
| six.raise_from(ValueError(message), ex) | ||
|
||
| elif client_secret: | ||
| credential = client_secret | ||
| else: | ||
| raise ValueError("Either `client_certificate` or `client_secret` must be provided") | ||
|
|
||
| super(OnBehalfOfCredential, self).__init__(client_id, credential, tenant_id=tenant_id, **kwargs) | ||
| self._assertion = user_assertion | ||
| self._auth_record = None # type: Optional[AuthenticationRecord] | ||
|
|
||
| @wrap_exceptions | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,16 +30,18 @@ class OnBehalfOfCredential(AsyncContextManager, GetTokenMixin): | |
|
|
||
| :param str tenant_id: ID of the service principal's tenant. Also called its "directory" ID. | ||
| :param str client_id: the service principal's client ID | ||
| :param client_credential: a credential to authenticate the service principal, either one of its client secrets (a | ||
| string) or the bytes of a certificate in PEM or PKCS12 format including the private key | ||
| :paramtype client_credential: str or bytes | ||
| :param str user_assertion: the access token the credential will use as the user assertion when requesting | ||
| on-behalf-of tokens | ||
| :keyword str client_secret: Optional. client secrets to authenticate the service principal. | ||
| Either client_secret or client_certificate must be provided. | ||
| :keyword bytes client_certificate: Optional. the bytes of a certificate in PEM or PKCS12 format including | ||
| the private key to authenticate the service principal. Either client_secret or client_certificate must | ||
| be provided. | ||
| :keyword str user_assertion: Required. the access token the credential will use as the user assertion when | ||
| requesting on-behalf-of tokens | ||
|
|
||
| :keyword str authority: Authority of an Azure Active Directory endpoint, for example "login.microsoftonline.com", | ||
| the authority for Azure Public Cloud (which is the default). :class:`~azure.identity.AzureAuthorityHosts` | ||
| defines authorities for other clouds. | ||
| :keyword password: a certificate password. Used only when **client_credential** is certificate bytes. If this value | ||
| :keyword password: a certificate password. Used only when **client_certificate** is provided. If this value | ||
| is a unicode string, it will be encoded as UTF-8. If the certificate requires a different encoding, pass | ||
| appropriately encoded bytes instead. | ||
| :paramtype password: str or bytes | ||
|
|
@@ -49,31 +51,37 @@ def __init__( | |
| self, | ||
| tenant_id: str, | ||
| client_id: str, | ||
| client_credential: "Union[bytes, str]", | ||
| *, | ||
| client_certificate: bytes = None, | ||
| client_secret: str = None, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wish we had already dropped 2.7 support so we could do this for the sync credential 🥲 |
||
| user_assertion: str, | ||
| **kwargs: "Any" | ||
| ) -> None: | ||
| super().__init__() | ||
| validate_tenant_id(tenant_id) | ||
|
|
||
| if isinstance(client_credential, bytes): | ||
| self._assertion = user_assertion | ||
| if not self._assertion: | ||
| raise ValueError("'user_assertion' is required.") | ||
|
|
||
| if client_certificate: | ||
| try: | ||
| cert = get_client_credential(None, kwargs.pop("password", None), client_credential) | ||
| cert = get_client_credential(None, kwargs.pop("password", None), client_certificate) | ||
| except ValueError as ex: | ||
| message = ( | ||
| '"client_credential" should be either a client secret (a string)' | ||
| + " or the bytes of a certificate in PEM or PKCS12 format" | ||
| '"client_certificate" should be the bytes of a certificate in PEM or PKCS12 format' | ||
| ) | ||
| raise ValueError(message) from ex | ||
| self._client_credential = AadClientCertificate( | ||
| cert["private_key"], password=cert.get("passphrase") | ||
| ) # type: Union[str, AadClientCertificate] | ||
| elif client_secret: | ||
| self._client_credential = client_secret | ||
| else: | ||
| self._client_credential = client_credential | ||
| raise ValueError("Either `client_certificate` or `client_secret` must be provided") | ||
xiangyan99 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # note AadClient handles "authority" and any pipeline kwargs | ||
| self._client = AadClient(tenant_id, client_id, **kwargs) | ||
| self._assertion = user_assertion | ||
|
|
||
| async def __aenter__(self): | ||
| await self._client.__aenter__() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this discussed offline after the archboard meeting? It seemed to me like we didn't think the name was great, but that it would be difficult to split this parameter into two since at least one is required. I do see that there's a check and error if neither is provided.
In terms of precedent, the CertificateCredential has a somewhat similar story ("provide one optional parameter, or provide another"). The design there doesn't seem ideal to me, although having an optional positional parameter does have the benefit of making it more prominent (I worry here that having
client_secretandclient_certificateas kwargs hides them too much)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with the information from Scott:
one possibility here is if they ultimately also allow OBO through managed identity, but that will likely require some changes from what we have now anyway.
We think it maybe make more sense to move client_credential into kwargs as well to reserve the flexibility.
Then there is no reason to have a union type in kwargs w/ a confusing name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I wasn't in the meeting; I would have argued for keeping the single positional parameter having two valid value types. That seems plainly better to me and I think not unconventional for Python libraries. I'm surprised architects preferred two mutually exclusive kwargs with runtime errors for guidance.
I agree the CertificateCredential design is suboptimal. It ended up that way because 1.0 had
certificate_pathas a required positional parameter.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took offline.