Skip to content

Commit b0289c5

Browse files
authored
ADD: user username & password to get token from cognito (#61)
* feat: user username & password to get token from cognito * chore: update changelog to match with version * chore: change release order
1 parent d2c2f34 commit b0289c5

File tree

17 files changed

+410
-82
lines changed

17 files changed

+410
-82
lines changed

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [0.1.0] - 2022-04-14
8+
## [1.6.16] - 2022-07-25
9+
### Added
10+
- Added: use username & password to login to cognito to get the token
11+
### Fixed
912

13+
## [0.1.0] - 2022-04-14
1014
### Added
1115
- Added lambda for parsing metadata from Sounder SIPS L0 metadata files [#14](https://github.com/unity-sds/unity-data-services/issues/14)
12-
1316
### Fixed
1417
- Pushed docker image to ghcr.io

cumulus_lambda_functions/cumulus_dapa_client/__init__.py

Whitespace-only changes.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import json
2+
import logging
3+
import os
4+
5+
import requests
6+
7+
from cumulus_lambda_functions.lib.cognito_login.cognito_token_retriever import CognitoTokenRetriever
8+
from cumulus_lambda_functions.lib.constants import Constants
9+
10+
LOGGER = logging.getLogger(__name__)
11+
12+
13+
class DapaClient:
14+
def __init__(self):
15+
self.__token_retriever = CognitoTokenRetriever()
16+
self.__token = None
17+
self.__dapa_base_api = None
18+
self.__get_dapa_base_api()
19+
self.__verify_ssl = True
20+
21+
def with_verify_ssl(self, verify_ssl: bool):
22+
self.__verify_ssl = verify_ssl
23+
return self
24+
25+
def __get_dapa_base_api(self):
26+
if Constants.DAPA_API_KEY not in os.environ:
27+
raise ValueError(f'missing key: {Constants.DAPA_API_KEY}')
28+
self.__dapa_base_api = os.environ.get(Constants.DAPA_API_KEY)
29+
self.__dapa_base_api = self.__dapa_base_api[:-1] if self.__dapa_base_api.endswith('/') else self.__dapa_base_api
30+
return self
31+
32+
def __get_token(self):
33+
if self.__token is None:
34+
self.__token = self.__token_retriever.start()
35+
if self.__token is None:
36+
raise ValueError('unable to retrieve DAPA token')
37+
return self
38+
39+
def get_collection(self, collection_id: str):
40+
"""
41+
TODO need better endpoint to get exactly 1 collection
42+
TODO pagination?
43+
:param collection_id:
44+
:return:
45+
"""
46+
LOGGER.debug(f'getting collection details for: {collection_id}')
47+
self.__get_token()
48+
header = {'Authorization': f'Bearer {self.__token}'}
49+
dapa_collection_url = f'{self.__dapa_base_api}/am-uds-dapa/collections?limit=1000'
50+
response = requests.get(url=dapa_collection_url, headers=header, verify=self.__verify_ssl)
51+
if response.status_code > 400:
52+
raise RuntimeError(
53+
f'querying collections ends in error. status_code: {response.status_code}. url: {dapa_collection_url}. details: {response.text}')
54+
collections_result = json.loads(response.text)
55+
if 'features' not in collections_result:
56+
raise RuntimeError(f'missing features in response. invalid response: response: {collections_result}')
57+
collection_details = [each_collection for each_collection in collections_result['features'] if
58+
collection_id == each_collection["id"]]
59+
if len(collection_details) < 1:
60+
raise RuntimeError(f'unable to find collection in DAPA')
61+
return collection_details[0]
62+
63+
def get_granules(self, collection_id='*', limit=1000, offset=0, date_from='', date_to=''):
64+
"""
65+
TODO: pagination. getting only 1st 1k item
66+
:param collection_id:
67+
:param limit:
68+
:param offset:
69+
:param date_from:
70+
:param date_to:
71+
:return:
72+
"""
73+
dapa_granules_api = f'{self.__dapa_base_api}/am-uds-dapa/collections/{collection_id}/items?limit={limit}&offset={offset}'
74+
if date_from != '' or date_to != '':
75+
dapa_granules_api = f"{dapa_granules_api}&datetime={date_from if date_from != '' else '..'}/{date_to if date_to != '' else '..'}"
76+
LOGGER.debug(f'dapa_granules_api: {dapa_granules_api}')
77+
LOGGER.debug(f'getting granules for: {dapa_granules_api}')
78+
self.__get_token()
79+
header = {'Authorization': f'Bearer {self.__token}'}
80+
response = requests.get(url=dapa_granules_api, headers=header, verify=self.__verify_ssl)
81+
if response.status_code > 400:
82+
raise RuntimeError(
83+
f'querying granules ends in error. status_code: {response.status_code}. url: {dapa_granules_api}. details: {response.text}')
84+
granules_result = json.loads(response.text)
85+
if 'features' not in granules_result:
86+
raise RuntimeError(f'missing features in response. invalid response: response: {granules_result}')
87+
return granules_result['features']
88+
89+
def ingest_granules_w_cnm(self, cnm_ingest_body: dict) -> str:
90+
dapa_ingest_cnm_api = f'{self.__dapa_base_api}/am-uds-dapa/collections/'
91+
LOGGER.debug(f'getting granules for: {dapa_ingest_cnm_api}')
92+
self.__get_token()
93+
header = {
94+
'Authorization': f'Bearer {self.__token}',
95+
'Content-Type': 'application/json',
96+
}
97+
response = requests.put(url=dapa_ingest_cnm_api, headers=header, verify=self.__verify_ssl,
98+
data=json.dumps(cnm_ingest_body))
99+
if response.status_code > 400:
100+
raise RuntimeError(
101+
f'querying granules ingestion ends in error. status_code: {response.status_code}. url: {dapa_ingest_cnm_api}. details: {response.text}')
102+
granules_result = response.text
103+
return granules_result

cumulus_lambda_functions/cumulus_download_granules/download_granules.py

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@
22
import logging
33
import os
44

5-
import requests
6-
5+
from cumulus_lambda_functions.cumulus_dapa_client.dapa_client import DapaClient
76
from cumulus_lambda_functions.lib.aws.aws_s3 import AwsS3
87
from cumulus_lambda_functions.lib.utils.file_utils import FileUtils
98

109
LOGGER = logging.getLogger(__name__)
1110

1211

1312
class DownloadGranules:
14-
DAPA_API_KEY = 'DAPA_API'
15-
UNITY_BEARER_TOKEN_KEY = 'UNITY_BEARER_TOKEN'
1613
COLLECTION_ID_KEY = 'COLLECTION_ID'
1714
DOWNLOAD_DIR_KEY = 'DOWNLOAD_DIR'
1815

@@ -22,23 +19,19 @@ class DownloadGranules:
2219
VERIFY_SSL_KEY = 'VERIFY_SSL'
2320

2421
def __init__(self):
25-
self.__dapa_api = ''
26-
self.__unity_bearer_token = ''
2722
self.__collection_id = ''
2823
self.__date_from = ''
2924
self.__date_to = ''
30-
self.__limit = 100
25+
self.__limit = 1000
3126
self.__download_dir = '/tmp'
3227
self.__verify_ssl = True
3328
self.__s3 = AwsS3()
3429

3530
def __set_props_from_env(self):
36-
missing_keys = [k for k in [self.DAPA_API_KEY, self.COLLECTION_ID_KEY, self.DOWNLOAD_DIR_KEY, self.UNITY_BEARER_TOKEN_KEY] if k not in os.environ]
31+
missing_keys = [k for k in [self.COLLECTION_ID_KEY, self.DOWNLOAD_DIR_KEY] if k not in os.environ]
3732
if len(missing_keys) > 0:
3833
raise ValueError(f'missing environment keys: {missing_keys}')
3934

40-
self.__dapa_api = os.environ.get(self.DAPA_API_KEY)
41-
self.__unity_bearer_token = os.environ.get(self.UNITY_BEARER_TOKEN_KEY)
4235
self.__collection_id = os.environ.get(self.COLLECTION_ID_KEY)
4336
self.__download_dir = os.environ.get(self.DOWNLOAD_DIR_KEY)
4437
self.__download_dir = self.__download_dir[:-1] if self.__download_dir.endswith('/') else self.__download_dir
@@ -52,25 +45,6 @@ def __set_props_from_env(self):
5245
self.__verify_ssl = os.environ.get(self.VERIFY_SSL_KEY, 'TRUE').strip().upper() == 'TRUE'
5346
return self
5447

55-
def __generate_dapa_url(self):
56-
self.__dapa_api = self.__dapa_api[:-1] if self.__dapa_api.endswith('/') else self.__dapa_api
57-
dapa_granules_api = f'{self.__dapa_api}/am-uds-dapa/collections/{self.__collection_id}/items?limit={self.__limit}&offset=0'
58-
if self.__date_from != '' or self.__date_to != '':
59-
dapa_granules_api = f"{dapa_granules_api}&datetime={self.__date_from if self.__date_from != '' else '..'}/{self.__date_to if self.__date_to != '' else '..'}"
60-
LOGGER.debug(f'dapa_granules_api: {dapa_granules_api}')
61-
return dapa_granules_api
62-
63-
def __get_granules(self, dapa_granules_api): # TODO pagination if needed
64-
LOGGER.debug(f'getting granules for: {dapa_granules_api}')
65-
header = {'Authorization': f'Bearer {self.__unity_bearer_token}'}
66-
response = requests.get(url=dapa_granules_api, headers=header, verify=self.__verify_ssl)
67-
if response.status_code > 400:
68-
raise RuntimeError(f'querying granules ends in error. status_code: {response.status_code}. url: {dapa_granules_api}. details: {response.text}')
69-
granules_result = json.loads(response.text)
70-
if 'features' not in granules_result:
71-
raise RuntimeError(f'missing features in response. invalid response: response: {granules_result}')
72-
return granules_result['features']
73-
7448
def __get_downloading_urls(self, granules_result: list):
7549
if len(granules_result) < 1:
7650
LOGGER.warning(f'cannot find any granules')
@@ -121,8 +95,8 @@ def start(self):
12195
self.__set_props_from_env()
12296
LOGGER.debug(f'creating download dir: {self.__download_dir}')
12397
FileUtils.mk_dir_p(self.__download_dir)
124-
dapa_granules_api = self.__generate_dapa_url()
125-
granules_result = self.__get_granules(dapa_granules_api)
98+
dapa_client = DapaClient().with_verify_ssl(self.__verify_ssl)
99+
granules_result = dapa_client.get_granules(self.__collection_id, self.__limit, 0, self.__date_from, self.__date_to)
126100
downloading_urls = self.__get_downloading_urls(granules_result)
127101
error_list = []
128102
for each in downloading_urls:

cumulus_lambda_functions/cumulus_upload_granules/upload_granules.py

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77

88
import requests
99

10+
from cumulus_lambda_functions.cumulus_dapa_client.dapa_client import DapaClient
1011
from cumulus_lambda_functions.lib.aws.aws_s3 import AwsS3
1112

1213
LOGGER = logging.getLogger(__name__)
1314

1415

1516
class UploadGranules:
16-
DAPA_API_KEY = 'DAPA_API'
17-
UNITY_BEARER_TOKEN_KEY = 'UNITY_BEARER_TOKEN'
1817
COLLECTION_ID_KEY = 'COLLECTION_ID'
1918
PROVIDER_ID_KEY = 'PROVIDER_ID'
2019
UPLOAD_DIR_KEY = 'UPLOAD_DIR'
@@ -24,8 +23,6 @@ class UploadGranules:
2423
DELETE_FILES_KEY = 'DELETE_FILES'
2524

2625
def __init__(self):
27-
self.__dapa_api = ''
28-
self.__unity_bearer_token = ''
2926
self.__collection_id = ''
3027
self.__collection_details = {}
3128
self.__uploading_granules = []
@@ -38,13 +35,10 @@ def __init__(self):
3835
self.__raw_files = []
3936

4037
def __set_props_from_env(self):
41-
missing_keys = [k for k in [self.DAPA_API_KEY, self.COLLECTION_ID_KEY, self.PROVIDER_ID_KEY, self.UPLOAD_DIR_KEY, self.UNITY_BEARER_TOKEN_KEY, self.STAGING_BUCKET_KEY] if k not in os.environ]
38+
missing_keys = [k for k in [self.COLLECTION_ID_KEY, self.PROVIDER_ID_KEY, self.UPLOAD_DIR_KEY, self.STAGING_BUCKET_KEY] if k not in os.environ]
4239
if len(missing_keys) > 0:
4340
raise ValueError(f'missing environment keys: {missing_keys}')
4441

45-
self.__dapa_api = os.environ.get(self.DAPA_API_KEY)
46-
self.__dapa_api = self.__dapa_api[:-1] if self.__dapa_api.endswith('/') else self.__dapa_api
47-
self.__unity_bearer_token = os.environ.get(self.UNITY_BEARER_TOKEN_KEY)
4842
self.__collection_id = os.environ.get(self.COLLECTION_ID_KEY)
4943
self.__provider_id = os.environ.get(self.PROVIDER_ID_KEY)
5044
self.__staging_bucket = os.environ.get(self.STAGING_BUCKET_KEY)
@@ -56,25 +50,6 @@ def __set_props_from_env(self):
5650
self.__delete_files = os.environ.get(self.DELETE_FILES_KEY, 'FALSE').strip().upper() == 'TRUE'
5751
return self
5852

59-
def __get_collection_stac(self):
60-
LOGGER.debug(f'getting collection details for: {self.__collection_id}')
61-
header = {'Authorization': f'Bearer {self.__unity_bearer_token}'}
62-
dapa_collection_url = f'{self.__dapa_api}/am-uds-dapa/collections?limit=1000'
63-
# TODO need better endpoint to get exactly 1 collection
64-
# TODO pagination?
65-
response = requests.get(url=dapa_collection_url, headers=header, verify=self.__verify_ssl)
66-
if response.status_code > 400:
67-
raise RuntimeError(f'querying collections ends in error. status_code: {response.status_code}. url: {dapa_collection_url}. details: {response.text}')
68-
collections_result = json.loads(response.text)
69-
if 'features' not in collections_result:
70-
raise RuntimeError(f'missing features in response. invalid response: response: {collections_result}')
71-
print(self.__collection_id)
72-
collection_details = [each_collection for each_collection in collections_result['features'] if self.__collection_id == each_collection["id"]]
73-
if len(collection_details) < 1:
74-
raise RuntimeError(f'unable to find collection in DAPA')
75-
self.__collection_details = collection_details[0]
76-
return self
77-
7853
def __sort_granules(self):
7954
file_regex_list = {k['type']: k['href'].split('___')[-1] for k in self.__collection_details['links'] if not k['title'].endswith('cmr.xml')}
8055
granule_id_extraction = self.__collection_details['summaries']['granuleIdExtraction']
@@ -111,20 +86,6 @@ def __upload_granules(self, granule_assets: dict, granule_id: str):
11186
href_dict['href'] = s3_url
11287
return self
11388

114-
def __execute_dapa_cnm_ingestion(self, cnm_ingest_body: dict):
115-
dapa_ingest_cnm_api = f'{self.__dapa_api}/am-uds-dapa/collections/'
116-
LOGGER.debug(f'getting granules for: {dapa_ingest_cnm_api}')
117-
header = {
118-
'Authorization': f'Bearer {self.__unity_bearer_token}',
119-
'Content-Type': 'application/json',
120-
}
121-
response = requests.put(url=dapa_ingest_cnm_api, headers=header, verify=self.__verify_ssl, data=json.dumps(cnm_ingest_body))
122-
if response.status_code > 400:
123-
raise RuntimeError(
124-
f'querying granules ingestion ends in error. status_code: {response.status_code}. url: {dapa_ingest_cnm_api}. details: {response.text}')
125-
granules_result = response.text
126-
return granules_result
127-
12889
def start(self):
12990
"""
13091
@@ -139,7 +100,8 @@ def start(self):
139100
self.__set_props_from_env()
140101
LOGGER.debug(f'listing files recursively in dir: {self.__upload_dir}')
141102
self.__raw_files = glob(f'{self.__upload_dir}/**/*', recursive=True)
142-
self.__get_collection_stac()
103+
dapa_client = DapaClient().with_verify_ssl(self.__verify_ssl)
104+
self.__collection_details = dapa_client.get_collection(self.__collection_id)
143105
on_disk_granules = self.__sort_granules()
144106
LOGGER.debug(f'on_disk_granules: {on_disk_granules}')
145107
dapa_body_granules = []
@@ -156,5 +118,5 @@ def start(self):
156118
"features": dapa_body_granules
157119
}
158120
LOGGER.debug(f'dapa_body_granules: {dapa_body}')
159-
dapa_ingest_reuslt = self.__execute_dapa_cnm_ingestion(dapa_body)
160-
return dapa_ingest_reuslt
121+
dapa_ingest_result = dapa_client.ingest_granules_w_cnm(dapa_body)
122+
return dapa_ingest_result
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import logging
2+
from typing import Union
3+
4+
from botocore.exceptions import ClientError
5+
6+
from cumulus_lambda_functions.lib.aws.aws_cred import AwsCred
7+
8+
LOGGER = logging.getLogger()
9+
10+
11+
class AwsParamStore(AwsCred):
12+
def __init__(self):
13+
super().__init__()
14+
self.__ssm_client = self.get_client('ssm')
15+
self.__ssm = None
16+
17+
def set_param(self, key: str, val: str, encrypted: bool = True):
18+
self.__ssm_client.put_parameter(Name=key, Value=val,
19+
Type='SecureString' if encrypted else 'String',
20+
Overwrite=True)
21+
22+
def get_param(self, param_name: str) -> Union[str, None]:
23+
"""
24+
Ref: https://stackoverflow.com/questions/46063138/how-can-i-import-the-boto3-ssm-parameternotfound-exception
25+
on how to catch ParameterNotFound exception
26+
27+
:param param_name: named of parameter in Parameter store
28+
:return: plain value of the parameter or None if some exception.
29+
"""
30+
try:
31+
param_response = self.__ssm_client.get_parameter(Name=param_name, WithDecryption=True)
32+
if 'ResponseMetadata' not in param_response or \
33+
'HTTPStatusCode' not in param_response['ResponseMetadata'] or \
34+
param_response['ResponseMetadata']['HTTPStatusCode'] != 200 or \
35+
'Parameter' not in param_response or \
36+
'Value' not in param_response['Parameter']:
37+
return None
38+
except ClientError:
39+
LOGGER.exception('cannot get parameter store value for %s', param_name)
40+
return None
41+
return param_response['Parameter']['Value']

cumulus_lambda_functions/lib/cognito_login/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)