Skip to content
This repository has been archived by the owner on Sep 29, 2023. It is now read-only.

Add verify and proxies to context #142

Merged
merged 3 commits into from
May 15, 2018
Merged

Add verify and proxies to context #142

merged 3 commits into from
May 15, 2018

Conversation

lmazuel
Copy link
Contributor

@lmazuel lmazuel commented May 7, 2018

Hi,

I work with @yugangw-msft in the SDK/CLI team. I'm migrating the Autorest internal authentication system to use 100% of ADAL, so all SDK/CLI queries pass through it.
Part of my migration, I realized that my current implementation support verify/proxies as context parameters, where ADAL does not. This PR is just passing through requests verify/proxies. No breaking changes, just let the options pass to requests. Documentation strings are taken from requests directly.

Reference:
http://docs.python-requests.org/en/master/user/advanced/#proxies

Let me know if you have questions (internal email or here)
Thank you!

Laurent

@rayluo
Copy link
Collaborator

rayluo commented May 7, 2018

Thanks Laurent! We will take a look into it soon! Stay tuned.

:param verify_ssl: (optional) requests verify. Either a boolean, in which case it
controls whether we verify the server's TLS certificate, or a string, in which
case it must be a path to a CA bundle to use. This value is ignored if env
variable ADAL_PYTHON_SSL_NO_VERIFY is used.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more natural that we do the other way around, i.e. env var will be ignored if function argument is explicitly provided? I know there might not be an industry standard on this behavior but, it happens that the Python standard lib urllib uses the same approach as what I suggested here:

Alternatively, the optional proxies argument may be used to explicitly specify proxies. It must be a dictionary mapping scheme names to proxy URLs, where an empty dictionary causes no proxies to be used, and None (the default value) causes environmental proxy settings to be used as discussed above. For example: ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @rayluo on this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, in progress

case it must be a path to a CA bundle to use. This value is ignored if env
variable ADAL_PYTHON_SSL_NO_VERIFY is used.
:param proxies: (optional) requests proxies. Dictionary mapping protocol to the URL
of the proxy.
Copy link
Collaborator

@rayluo rayluo May 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is true that this is the same concise docstring used by requests library. But they also provide intensive documentation to provide the examples on what "dictionary mapping" actually looks like. Since we don't currently have those documentation effort, I would suggest we at least add one more sentence here. Either explicitly mentions an example:

Its content will look like proxies = { 'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080', }

Or simply add a link to its official documentation in requests (since we are exposing this requests behavior anyway).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer external link, scale better in the long term IMHO :)

@rayluo rayluo requested a review from yugangw-msft May 8, 2018 00:51
@yugangw-msft
Copy link
Contributor

yugangw-msft commented May 8, 2018

Agree with existing code review comments. LGTM on the rest

@@ -95,6 +101,7 @@ def __init__(
'options': GLOBAL_ADAL_OPTIONS,
'api_version': api_version,
'verify_ssl': None if env_value is None else not env_value, # mainly for tracing through proxy
Copy link
Contributor

@yugangw-msft yugangw-msft May 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this line will need to be updated; otherwise the argument value will not be propagated particularly when it is a string to a bundle

Copy link
Contributor Author

@lmazuel lmazuel May 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the argument value was passed as default parameter in the os.environ, so yes, it was propagated, but I will simplify this line

@lmazuel
Copy link
Contributor Author

lmazuel commented May 8, 2018

@rayluo @yugangw-msft I think the logic is better now, let me know.

Also, I "fixed" a weird scenario. The way the code was written, an empty ADAL_PYTHON_SSL_NO_VERIFY was considered as verify (since bool(not '') == True). Everything else is still considered as "no verify" (even export ADAL_PYTHON_SSL_NO_VERIFY=no). Will be too dangerous IMHO to change the logic to actually look at what is inside the string.

Copy link
Collaborator

@rayluo rayluo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that you called out the way we use ADAL_PYTHON_SSL_NO_VERIFY is its presence only. In such case, I made further minor suggestion in the code. Please take a look and let me know.

Other things look good.

'''
self.authority = Authority(authority, validate_authority is None or validate_authority)
self._oauth2client = None
self.correlation_id = None
env_value = os.environ.get('ADAL_PYTHON_SSL_NO_VERIFY')
env_verify = False if 'ADAL_PYTHON_SSL_NO_VERIFY' in os.environ else None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about env_verify = 'ADAL_PYTHON_SSL_NO_VERIFY' not in os.version? Because None is conceptually NOT an expected value by requests' verify anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None is the actual default value of requests actually, if you look at the method signature:
https://github.com/requests/requests/blob/1e8a3142d2376e86d8f923840c0b8d361af7786b/requests/sessions.py#L449

This is done that way in the operation signature, so a not-None value override the verify static value in the Session object (which this one is True by default: https://github.com/requests/requests/blob/1e8a3142d2376e86d8f923840c0b8d361af7786b/requests/sessions.py#L373). Since you don't use a Session, that would be equivalent, but None is accurate as well in this context. As long as you don't decide to use a common Session in the context, our two codes are indeed equivalent :).
Tell me what you prefer

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I know that as a Python convention, many function technically uses None as default value. When I said "None is CONCEPTUALLY not an expected value" in this case, I mean the underlying documentation saying that:

  1. "verify can either be a boolean or a string";
  2. From a document perspective, it does not explicitly mention None, it just says "by default it will be set to True"; (By the way, if I were writing that function, I would probably actually define True as its default value: request(..., verify=True, ...)

Back to our case. Any future maintainer reading this line would think "why we differenciate False and None here? Do they make a difference?" and then they would need to dive into requests' doc. By explicitly coding a True or False as the value we care and use, we can avoid that mental exercise, if nothing else.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine with that, doing the change.

:param verify_ssl: (optional) requests verify. Either a boolean, in which case it
controls whether we verify the server's TLS certificate, or a string, in which
case it must be a path to a CA bundle to use. This value overrides env
variable ADAL_PYTHON_SSL_NO_VERIFY.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest to change "This value overrides env variable ADAL_..." to "If this value is None, we will use the presence of env variable ADAL_PYTHON_SSL_NO_VERIFY to turn off SSL verification."

You can help to proofread it. The goal is to also explicitly document the usage of ADAL_PYTHON_SSL_NO_VERIFY as using its presence (or not) as a boolean; and we are NOT using ADAL_PYTHON_SSL_NO_VERIFY as a string path to a CA bundle.

Copy link
Contributor Author

@lmazuel lmazuel May 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about:

If this value is not provided, and ADAL_PYTHON_SSL_NO_VERIFY env varaible is set, behavior is equivalent to verify_ssl=False

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good! :-)

Copy link
Collaborator

@rayluo rayluo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@rayluo rayluo merged commit 3765b34 into AzureAD:dev May 15, 2018
@abhidnya13 abhidnya13 mentioned this pull request May 15, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants