Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions hca/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ def special_cli_command(self, required_argument, optional_argument=""):
from __future__ import absolute_import, division, print_function, unicode_literals

import os, types, collections, typing, json, errno, base64, argparse
import time

import jwt

try:
from inspect import signature, Signature, Parameter
Expand Down Expand Up @@ -296,12 +299,13 @@ def login(self, access_token=""):
if access_token:
credentials = argparse.Namespace(token=access_token, refresh_token=None, id_token=None)
else:
scopes = ["https://www.googleapis.com/auth/plus.me",
"https://www.googleapis.com/auth/userinfo.email"]
scopes = ["openid", "email", "offline_access"]

from google_auth_oauthlib.flow import InstalledAppFlow
flow = InstalledAppFlow.from_client_config(self.application_secrets, scopes=scopes)
credentials = flow.run_local_server()
msg = "Authentication successful. Please close this tab and run HCA CLI commands in the terminal."
credentials = flow.run_local_server(success_message=msg, audience="https://dss.dev.data.humancellatlas.org/")

# TODO: (akislyuk) test token autorefresh on expiration
self.config.oauth2_token = dict(access_token=credentials.token,
refresh_token=credentials.refresh_token,
Expand Down Expand Up @@ -332,11 +336,37 @@ def _get_oauth_token_from_service_account_credentials(self):
r.session.close()
return credentials.token, credentials.expiry

def _get_jwt_from_service_account_credentials(self):
assert 'GOOGLE_APPLICATION_CREDENTIALS' in os.environ
service_account_credentials_filename = os.environ['GOOGLE_APPLICATION_CREDENTIALS']
if not os.path.isfile(service_account_credentials_filename):
msg = 'File "{}" referenced by the GOOGLE_APPLICATION_CREDENTIALS environment variable does not exist'
raise Exception(msg.format(service_account_credentials_filename))
with open(service_account_credentials_filename) as fh:
service_credentials = json.load(fh)

audience = "https://dev.data.humancellatlas.org/"
iat = time.time()
exp = iat + 3600
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Probably good to parameterize this through a kwarg to this method.

Copy link
Copy Markdown
Collaborator Author

@Bento007 Bento007 Sep 7, 2018

Choose a reason for hiding this comment

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

will fix once we figure out how to store and reuse the token.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

payload = {'iss': service_credentials["client_email"],
'sub': service_credentials["client_email"],
'aud': audience,
'iat': iat,
'exp': exp,
'email': service_credentials["client_email"],
'scope': ['email', 'openid', 'offline_access']
}
payload['https://auth.data.humancellatlas.org/group'] = 'hca'
additional_headers = {'kid': service_credentials["private_key_id"]}
signed_jwt = jwt.encode(payload, service_credentials["private_key"], headers=additional_headers,
algorithm='RS256').decode()
return signed_jwt, exp

def get_authenticated_session(self):
if self._authenticated_session is None:
oauth2_client_data = self.application_secrets["installed"]
if 'GOOGLE_APPLICATION_CREDENTIALS' in os.environ:
token, expires_at = self._get_oauth_token_from_service_account_credentials()
token, expires_at = self._get_jwt_from_service_account_credentials()
# TODO: (akislyuk) figure out the right strategy for persisting the service account oauth2 token
self._authenticated_session = OAuth2Session(client_id=oauth2_client_data["client_id"],
token=dict(access_token=token),
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
boto3 >= 1.7, < 1.8
crcmod >= 1.7, < 2
cryptography==2.3.1
google-auth >= 1.0.2, < 2
google-auth-oauthlib >= 0.1, < 2
Jinja2 >= 2.9, < 3
Expand All @@ -12,3 +13,4 @@ dcplib >= 1.3.2, < 2
argcomplete >= 1.9.3, < 2
commonmark >= 0.7.4, < 0.8
docutils >= 0.14, < 1
PyJWT >= 1.6.4
20 changes: 19 additions & 1 deletion test/integration/dss/test_dss_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def test_search(self, limit=128):
break

@reset_tweak_changes
def test_python_login_logout(self):
def test_python_login_logout_service_acount(self):
client = hca.dss.DSSClient()
query = {'bool': {}}
resp = client.put_subscription(es_query=query, callback_url="https://www.example.com", replica="aws")
Expand All @@ -274,9 +274,27 @@ def test_python_login_logout(self):
config = hca.get_config()

self.assertEqual(config.oauth2_token.access_token, access_token)
client.logout()
self.assertNotIn("oauth2_token", config)

@unittest.skipIf(True, "Manual Test")
@reset_tweak_changes
def test_python_login_logout_user_account(self):
client = hca.dss.DSSClient()
config = hca.get_config()

client.logout()
self.assertNotIn("oauth2_token", config)

client.login()
self.assertIn("oauth2_token", config)

query = {'bool': {}}
resp = client.put_subscription(es_query=query, callback_url="https://www.example.com", replica="aws")
self.assertIn("uuid", resp)
client.delete_subscription(uuid=resp["uuid"], replica="aws")

client.logout()
self.assertNotIn("oauth2_token", config)


Expand Down