Skip to content
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

[Feature/podaac-6307] Adjust authentication to use earthaccess instead of calling urs.earthdata api directly #182

Closed
wants to merge 11 commits into from
Closed
2 changes: 1 addition & 1 deletion .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
run: |
poetry run pytest -m "not regression and not token"
- name: netrc-gen
uses: extractions/netrc@v1
uses: extractions/netrc@v2
with:
machine: urs.earthdata.nasa.gov
username: ${{ secrets.EDL_OPS_USERNAME }}
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
### Fixed
- **PODAAC-6303 (issues/167)**
- Fixed issue where -gr and -sd/-ed (temporal) cannot be used together as a query
### Changed
- **PODAAC-6305 (issues/160)**
- Fixed issue when -gr and cycle are used together, and when sometimes the granule name is a subset of what's found in CMR (`SWOT_GPR_2PfP474_001_20230328_220206_20230328_225311` in `SWOT_GPR_2PfP474_002_20230328_225311_20230328_234417_swot`)
- **PODAAC-6307 (issues/175)**
- Changed token generation to come from earthaccess
- Remove token listing test and function since it has become obsolete
### Removed
- Removed python 3.7 from supported builds

Expand Down
1,390 changes: 1,223 additions & 167 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ packages = [
]

[tool.poetry.dependencies]
python = "^3.7"
python = ">=3.10,<4.0"
requests = "^2.27.1"
tenacity = "^8.0.1"
packaging = "^23.0"
harmony-py = "^0.4.12"
earthaccess = "^0.14.0"

[tool.poetry.dev-dependencies]
pytest = "^7.1.2"
Expand Down
90 changes: 13 additions & 77 deletions subscriber/podaac_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from dateutil.parser import *
import functools
from packaging import version
import earthaccess

import requests
import tenacity
Expand Down Expand Up @@ -93,87 +94,22 @@ def setup_earthdata_login_auth(endpoint):
request.install_opener(opener)



def get_token(url: str) -> str:
tokens = list_tokens(url)
if len(tokens) == 0 :
return create_token(url)
def get_token() -> str:
earthaccess.login()
token_obj = earthaccess.get_edl_token()
if isinstance(token_obj, dict) and 'access_token' in token_obj:
access_token = token_obj.get('access_token')
else:
return tokens[0]

###############################################################################
# GET TOKEN FROM CMR
###############################################################################
@tenacity.retry(wait=tenacity.wait_random_exponential(multiplier=1, max=60),
stop=tenacity.stop_after_attempt(3),
reraise=True,
retry=(tenacity.retry_if_result(lambda x: x == ''))
)
def create_token(url: str) -> str:
try:
token: str = ''
username, _, password = netrc.netrc().authenticators(edl)
headers: Dict = {'Accept': 'application/json'} # noqa E501


resp = requests.post(url+"/token", headers=headers, auth=HTTPBasicAuth(username, password))
response_content: Dict = json.loads(resp.content)
if "error" in response_content:
if response_content["error"] == "max_token_limit":
logging.error("Max tokens acquired from URS. Using existing token")
tokens=list_tokens(url)
return tokens[0]
token = response_content['access_token']

# Add better error handling there
# Max tokens
# Wrong Username/Passsword
# Other
except: # noqa E722
logging.warning("Error getting the token - check user name and password", exc_info=True)
return token


###############################################################################
# DELETE TOKEN FROM CMR
###############################################################################
def delete_token(url: str, token: str) -> bool:
try:
username, _, password = netrc.netrc().authenticators(edl)
headers: Dict = {'Accept': 'application/json'}
resp = requests.post(url+"/revoke_token",params={"token":token}, headers=headers, auth=HTTPBasicAuth(username, password))

if resp.status_code == 200:
logging.info("EDL token successfully deleted")
return True
else:
logging.info("EDL token deleting failed.")

except: # noqa E722
logging.warning("Error deleting the token", exc_info=True)

return False

def list_tokens(url: str):
try:
tokens = []
username, _, password = netrc.netrc().authenticators(edl)
headers: Dict = {'Accept': 'application/json'} # noqa E501
resp = requests.get(url+"/tokens", headers=headers, auth=HTTPBasicAuth(username, password))
response_content = json.loads(resp.content)

for x in response_content:
tokens.append(x['access_token'])

except: # noqa E722
logging.warning("Error getting the token - check user name and password", exc_info=True)
return tokens
raise KeyError(f'Issue with getting `access_token` from earthaccess.get_edl_token()\n'
f'token_obj = {token_obj}')
return access_token


def refresh_token(old_token: str):
setup_earthdata_login_auth(edl)
delete_token(token_url,old_token)
return get_token(token_url)
# Directly calling get_token to refresh the token since earthAccess internally can refresh tokens
# https://github.com/nsidc/earthaccess/commit/be1ec48a213cd9158c7d6a7542a56477cfc14d3e
# https://github.com/nsidc/earthaccess/pull/782
return get_token()


def validate(args):
Expand Down
2 changes: 1 addition & 1 deletion subscriber/podaac_data_downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def run(args=None):
exit(1)

pa.setup_earthdata_login_auth(edl)
token = pa.get_token(token_url)
token = pa.get_token()

data_path = args.outputDirectory
if not isdir(data_path):
Expand Down
2 changes: 1 addition & 1 deletion subscriber/podaac_data_subscriber.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def run(args=None):
exit(1)

pa.setup_earthdata_login_auth(edl)
token = pa.get_token(token_url)
token = pa.get_token()

mins = args.minutes # In this case download files ingested in the last 60 minutes -- change this to whatever setting is needed
provider = args.provider
Expand Down
36 changes: 1 addition & 35 deletions tests/test_token_regression.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,13 @@
import pytest
import os
from os.path import exists

from subscriber import podaac_access as pa
import shutil
from pathlib import Path

@pytest.mark.token
def setup_function(method):
# Deletes all known tokens
tokens = pa.list_tokens(pa.token_url)
for x in tokens:
pa.delete_token(pa.token_url, x)


@pytest.mark.token
def teardown_function(method):
# Deletes all known tokens
tokens = pa.list_tokens(pa.token_url)
for x in tokens:
pa.delete_token(pa.token_url, x)

# REGRESSION TEST CURRENTLY REQUIRES A .NETRC file for CMR/Data Download
# token API can be found here: https://wiki.earthdata.nasa.gov/display/EL/API+Documentation
# explore https://urs.earthdata.nasa.gov/documentation/for_integrators/api_documentation#/oauth/token
@pytest.mark.token
def test_list_tokens():
tokens = pa.list_tokens(pa.token_url)
assert len(tokens) == 0
pa.get_token(pa.token_url)
tokens = pa.list_tokens(pa.token_url)
assert len(tokens) == 1

@pytest.mark.token
def test_edl_getToken():
token = pa.get_token(pa.token_url)
token = pa.get_token()
assert token != ""
token = pa.refresh_token(token)
assert token != ""
tokens = pa.list_tokens(pa.token_url)

assert len(tokens) == 1
for x in tokens:
assert x != ""

assert True == pa.delete_token(pa.token_url, token)
Loading