From 556e10ef58261fd910b459eb5f7920565c716451 Mon Sep 17 00:00:00 2001 From: Andrew Scribner Date: Fri, 25 Feb 2022 15:45:20 -0500 Subject: [PATCH 1/2] fix: kf_authentication.py fails for some urls The previous iteration worked if the url passed to `kubeflow_login` was the kubeflow host domain (eg: `http://my.kubeflow/`), but not if the url was any other valid url under the domain (eg: `http://my.kubeflow/pipelines`). This was because some of the `request.get`s in the authentication flow needed the domain rather than the target url. This was missed because only the domain was tested when this was first written. These updates fix this problem - the authentication now works for all valid urls in the domain. Note that authentication will not work for any page that does not exist (eg: `http://my.kubeflow/somethingThatDoesNotExist` will **not** result in authentication) --- tests/kf_authentication.py | 89 +++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/tests/kf_authentication.py b/tests/kf_authentication.py index 261105df..79a254e1 100644 --- a/tests/kf_authentication.py +++ b/tests/kf_authentication.py @@ -8,9 +8,11 @@ logging.basicConfig(level=logging.DEBUG) -def kubeflow_login(host, username=None, password=None): +def kubeflow_login(url, username=None, password=None): """Completes the dex/oidc login flow, returning the authservice_session cookie.""" - host = host.rstrip('/') + parsed_url = urlparse(url) + url_base = f"{parsed_url.scheme}://{parsed_url.netloc}" + data = { 'login': username or os.getenv('KUBEFLOW_USERNAME', None), 'password': password or os.getenv('KUBEFLOW_PASSWORD', None), @@ -22,57 +24,54 @@ def kubeflow_login(host, username=None, password=None): " in KUBEFLOW_USERNAME/KUBEFLOW_PASSWORD environment variables." ) - # GET on host provides us a location header with the dex auth page - # and state for this session - response = requests.get(host, verify=False, allow_redirects=False) - validate_response_status_code(response, [302], f"Failed to connect to host site '{host}'.") - location = response.headers['location'] - - # Preserve only first portion of state if there are multiple, then rebuild - # the dex_url - state = parse_qs(location)['state'][0] - location_prefix, _ = location.split('state=') - location = location_prefix + f'state={state}' - dex_url = location - logging.debug(f"Redirected to dex_url of '{dex_url}'") - - # GET on the dex_url provides the dex auth login url we can use and a - # request token - response = requests.get(dex_url, verify=False, allow_redirects=False) - validate_response_status_code(response, [302], f"Failed to connect to dex_url '{dex_url}'.") - - if response.status_code != 302: - raise ValueError( - f"Failed to connect to host site. " - f"Got response {response.status_code}, expected 302" - ) - dex_login_partial_url = response.headers['location'] - dex_login_url = f'{host}{dex_login_partial_url}' - logging.debug(f"Got dex_login_url with request token of '{dex_login_url}") - - # Log in - response = requests.post(dex_login_url, data=data, verify=False, allow_redirects=False) + # GET on url redirects us to the dex_login_url including state for this session + response = requests.get( + url, + verify=False, + allow_redirects=True + ) + validate_response_status_code(response, [200], f"Failed to connect to url site '{url}'.") + dex_login_url = response.url + logging.debug(f"Redirected to dex_login_url of '{dex_login_url}'") + + # Log in, retrieving the redirection to the approval page + response = requests.post( + dex_login_url, + data=data, + verify=False, + allow_redirects=False + ) validate_response_status_code( - response, [301, 303], f"Failed to log into dex - are your credentials correct?" + response, [303], f"Failed to log into dex - are your credentials correct?" + ) + approval_endpoint = response.headers['location'] + dex_approval_url = url_base + approval_endpoint + logging.debug(f"Logged in with dex_approval_url of '{dex_approval_url}") + + # Get the OIDC approval code and state + response = requests.get( + dex_approval_url, + verify=False, + allow_redirects=False ) - dex_approval_partial_url = response.headers['location'] - dex_approval_url = f'{host}{dex_approval_partial_url}' - logging.debug(f"Got dex_approval_url of '{dex_approval_url}") - - # GET and return the authservice_session cookie - response = requests.get(dex_approval_url, verify=False, allow_redirects=False) validate_response_status_code( - response, [301, 303], f"Failed to connect to dex_approval_url '{dex_approval_url}'." + response, [303], f"Failed to connect to dex_approval_url '{dex_approval_url}'." ) - authservice_partial_url = response.headers['location'] - authservice_url = f"{host}{authservice_partial_url}" + authservice_endpoint = response.headers['location'] + authservice_url = url_base + authservice_endpoint logging.debug(f"Got authservice_url of '{authservice_url}'") - response = requests.get(authservice_url, verify=False, allow_redirects=False) + + # Access DEX OIDC path to generate session cookie + response = requests.get( + authservice_url, + verify=False, + allow_redirects=False, + ) validate_response_status_code( - response, [301, 302], f"Failed to connect to authservice_url '{authservice_url}'." + response, [302], f"Failed to connect to authservice_url '{authservice_url}'." ) - + return response.cookies['authservice_session'] From d86f0486b7fc2622e0294a1151e13127d7d5ddfb Mon Sep 17 00:00:00 2001 From: Andrew Scribner Date: Fri, 25 Feb 2022 17:52:06 -0500 Subject: [PATCH 2/2] fix: formatting --- tests/kf_authentication.py | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/tests/kf_authentication.py b/tests/kf_authentication.py index 79a254e1..873395f5 100644 --- a/tests/kf_authentication.py +++ b/tests/kf_authentication.py @@ -12,7 +12,7 @@ def kubeflow_login(url, username=None, password=None): """Completes the dex/oidc login flow, returning the authservice_session cookie.""" parsed_url = urlparse(url) url_base = f"{parsed_url.scheme}://{parsed_url.netloc}" - + data = { 'login': username or os.getenv('KUBEFLOW_USERNAME', None), 'password': password or os.getenv('KUBEFLOW_PASSWORD', None), @@ -25,35 +25,22 @@ def kubeflow_login(url, username=None, password=None): ) # GET on url redirects us to the dex_login_url including state for this session - response = requests.get( - url, - verify=False, - allow_redirects=True - ) + response = requests.get(url, verify=False, allow_redirects=True) validate_response_status_code(response, [200], f"Failed to connect to url site '{url}'.") dex_login_url = response.url logging.debug(f"Redirected to dex_login_url of '{dex_login_url}'") - + # Log in, retrieving the redirection to the approval page - response = requests.post( - dex_login_url, - data=data, - verify=False, - allow_redirects=False - ) + response = requests.post(dex_login_url, data=data, verify=False, allow_redirects=False) validate_response_status_code( response, [303], f"Failed to log into dex - are your credentials correct?" ) approval_endpoint = response.headers['location'] dex_approval_url = url_base + approval_endpoint logging.debug(f"Logged in with dex_approval_url of '{dex_approval_url}") - + # Get the OIDC approval code and state - response = requests.get( - dex_approval_url, - verify=False, - allow_redirects=False - ) + response = requests.get(dex_approval_url, verify=False, allow_redirects=False) validate_response_status_code( response, [303], f"Failed to connect to dex_approval_url '{dex_approval_url}'." ) @@ -61,7 +48,6 @@ def kubeflow_login(url, username=None, password=None): authservice_url = url_base + authservice_endpoint logging.debug(f"Got authservice_url of '{authservice_url}'") - # Access DEX OIDC path to generate session cookie response = requests.get( authservice_url, @@ -71,7 +57,7 @@ def kubeflow_login(url, username=None, password=None): validate_response_status_code( response, [302], f"Failed to connect to authservice_url '{authservice_url}'." ) - + return response.cookies['authservice_session']