Skip to content

Commit

Permalink
Merge 5903dbf into ed93a74
Browse files Browse the repository at this point in the history
  • Loading branch information
wphyojpl authored Nov 7, 2022
2 parents ed93a74 + 5903dbf commit 18e391c
Show file tree
Hide file tree
Showing 28 changed files with 890 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@
import os

from cumulus_lambda_functions.cumulus_wrapper.query_collections import CollectionsQuery
from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract
from cumulus_lambda_functions.lib.authorization.uds_authorizer_factory import UDSAuthorizerFactory
from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator
from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants
from cumulus_lambda_functions.lib.utils.lambda_api_gateway_utils import LambdaApiGatewayUtils

LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env())


class CumulusCollectionsDapa:
RESOURCE = 'COLLECTIONS'
ACTION = 'READ'

def __init__(self, event):
LOGGER.info(f'event: {event}')
self.__event = event
Expand All @@ -17,6 +23,8 @@ def __init__(self, event):
self.__offset = 0
self.__assign_values()
self.__page_number = (self.__offset // self.__limit) + 1
if 'COGNITO_UESR_POOL_ID' not in os.environ:
raise EnvironmentError('missing key: COGNITO_UESR_POOL_ID')
if 'CUMULUS_BASE' not in os.environ:
raise EnvironmentError('missing key: CUMULUS_BASE')
if 'CUMULUS_LAMBDA_PREFIX' not in os.environ:
Expand All @@ -29,6 +37,8 @@ def __init__(self, event):
self.__cumulus.with_limit(self.__limit)
self.__cumulus.with_page_number(self.__page_number)
self.__get_collection_id()
self.__lambda_utils = LambdaApiGatewayUtils(self.__event, self.__limit)
self.__authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory().get_instance(UDSAuthorizerFactory.cognito, user_pool_id=os.environ.get('COGNITO_UESR_POOL_ID'))

def __get_collection_id(self):
if 'pathParameters' not in self.__event:
Expand Down Expand Up @@ -62,14 +72,23 @@ def __get_size(self):

def __get_pagination_urls(self):
try:
pagination_links = LambdaApiGatewayUtils(self.__event, self.__limit).generate_pagination_links()
pagination_links = self.__lambda_utils.generate_pagination_links()
except Exception as e:
LOGGER.exception(f'error while generating pagination links')
return [{'message': f'error while generating pagination links: {str(e)}'}]
return pagination_links

def __setup_authorized_tenant_venue(self):
username = self.__lambda_utils.get_authorization_info()['username']
LOGGER.debug(f'query for user: {username}')
authorized_tenants = self.__authorizer.get_authorized_tenant(username, self.ACTION, self.RESOURCE)
for each_tenant in authorized_tenants:
self.__cumulus.with_tenant(each_tenant[DBConstants.tenant], each_tenant[DBConstants.tenant_venue])
return self

def start(self):
try:
self.__setup_authorized_tenant_venue()
cumulus_result = self.__cumulus.query_direct_to_private_api(self.__cumulus_lambda_prefix)
if 'server_error' in cumulus_result:
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@

from cumulus_lambda_functions.cumulus_stac.collection_transformer import CollectionTransformer
from cumulus_lambda_functions.cumulus_wrapper.query_collections import CollectionsQuery
from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract
from cumulus_lambda_functions.lib.authorization.uds_authorizer_factory import UDSAuthorizerFactory
from cumulus_lambda_functions.lib.aws.aws_lambda import AwsLambda
from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator
from cumulus_lambda_functions.lib.utils.lambda_api_gateway_utils import LambdaApiGatewayUtils

LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env())


class CumulusCreateCollectionDapa:
RESOURCE = 'COLLECTIONS'
ACTION = 'WRITE'

def __init__(self, event):
required_env = ['CUMULUS_LAMBDA_PREFIX', 'CUMULUS_WORKFLOW_SQS_URL']
required_env = ['CUMULUS_LAMBDA_PREFIX', 'CUMULUS_WORKFLOW_SQS_URL', 'COGNITO_UESR_POOL_ID']
if not all([k in os.environ for k in required_env]):
raise EnvironmentError(f'one or more missing env: {required_env}')
self.__event = event
Expand All @@ -24,6 +30,8 @@ def __init__(self, event):
self.__workflow_name = os.getenv('CUMULUS_WORKFLOW_NAME', 'CatalogGranule')
self.__provider_id = '' # TODO. need this?
self.__collection_creation_lambda_name = os.environ.get('COLLECTION_CREATION_LAMBDA_NAME', '').strip()
self.__lambda_utils = LambdaApiGatewayUtils(self.__event, 10)
self.__authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory().get_instance(UDSAuthorizerFactory.cognito, user_pool_id=os.environ.get('COGNITO_UESR_POOL_ID'))

def execute_creation(self):
try:
Expand Down Expand Up @@ -74,6 +82,11 @@ def execute_creation(self):
def start(self):
if 'body' not in self.__event:
raise ValueError(f'missing body in {self.__event}')
username = self.__lambda_utils.get_authorization_info()['username']
LOGGER.debug(f'query for user: {username}')
authorized_tenants = self.__authorizer.get_authorized_tenant(username, self.ACTION, self.RESOURCE)
# TODO compare project and project_venue vs the ones from request body.
# TODO project and project_venue in STAC body?
self.__request_body = json.loads(self.__event['body'])
LOGGER.debug(f'request body: {self.__request_body}')
validation_result = pystac.Collection.from_dict(self.__request_body).validate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@
import os

from cumulus_lambda_functions.cumulus_wrapper.query_granules import GranulesQuery
from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract
from cumulus_lambda_functions.lib.authorization.uds_authorizer_factory import UDSAuthorizerFactory
from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator
from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants
from cumulus_lambda_functions.lib.utils.lambda_api_gateway_utils import LambdaApiGatewayUtils

LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env())


class CumulusGranulesDapa:
RESOURCE = 'GRANULES'
ACTION = 'READ'

def __init__(self, event):
"""
{'resource': '/collections/observation/items',
Expand All @@ -27,6 +33,8 @@ def __init__(self, event):
self.__offset = 0
self.__assign_values()
self.__page_number = (self.__offset // self.__limit) + 1
if 'COGNITO_UESR_POOL_ID' not in os.environ:
raise EnvironmentError('missing key: COGNITO_UESR_POOL_ID')
if 'CUMULUS_BASE' not in os.environ:
raise EnvironmentError('missing key: CUMULUS_BASE')
if 'CUMULUS_LAMBDA_PREFIX' not in os.environ:
Expand All @@ -40,6 +48,8 @@ def __init__(self, event):
self.__cumulus.with_page_number(self.__page_number)
self.__get_time_range()
self.__get_collection_id()
self.__lambda_utils = LambdaApiGatewayUtils(self.__event, self.__limit)
self.__authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory().get_instance(UDSAuthorizerFactory.cognito, user_pool_id=os.environ.get('COGNITO_UESR_POOL_ID'))

def __get_collection_id(self):
if 'pathParameters' not in self.__event:
Expand Down Expand Up @@ -98,14 +108,25 @@ def __get_size(self):

def __get_pagination_urls(self):
try:
pagination_links = LambdaApiGatewayUtils(self.__event, self.__limit).generate_pagination_links()
pagination_links = self.__lambda_utils.generate_pagination_links()
except Exception as e:
LOGGER.exception(f'error while generating pagination links')
return [{'message': f'error while generating pagination links: {str(e)}'}]
return pagination_links

def __setup_authorized_tenant_venue(self):
username = self.__lambda_utils.get_authorization_info()['username']
LOGGER.debug(f'query for user: {username}')
authorized_tenants = self.__authorizer.get_authorized_tenant(username, self.ACTION, self.RESOURCE)
for each_tenant in authorized_tenants:
self.__cumulus.with_tenant(each_tenant[DBConstants.tenant], each_tenant[DBConstants.tenant_venue])
return self

def start(self):
try:
self.__setup_authorized_tenant_venue()
# TODO. cannot accept multiple collection_id. need single collection_id
# get project and project_venue from collection_id and compare against authorization table
cumulus_result = self.__cumulus.query_direct_to_private_api(self.__cumulus_lambda_prefix)
if 'server_error' in cumulus_result:
return {
Expand Down
8 changes: 8 additions & 0 deletions cumulus_lambda_functions/cumulus_wrapper/cumulus_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ def __init__(self, cumulus_base: str, cumulus_token: str):
'Authorization': f'Bearer {cumulus_token}'
}
self._conditions = ['status=completed']
self._authorized_tenants = []

def with_tenant(self, tenant: str, venue: str):
self._authorized_tenants.append({
'tenant': tenant,
'venue': venue,
})
return self

def with_page_number(self, page_number):
self._conditions.append(f'page={page_number}')
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from abc import ABC, abstractmethod


class UDSAuthorizorAbstract(ABC):
@abstractmethod
def add_authorized_group(self, action: [str], resource: [str], tenant: str, venue: str, ldap_group_name: str):
return

@abstractmethod
def delete_authorized_group(self, tenant: str, venue: str, ldap_group_name: str):
return

@abstractmethod
def list_authorized_groups_for(self, tenant: str, venue: str):
return

@abstractmethod
def update_authorized_group(self, action: [str], resource: [str], tenant: str, venue: str, ldap_group_name: str):
return

@abstractmethod
def get_authorized_tenant(self, username: str, action: str, resource: str) -> list:
return []
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import logging
import os

from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract
from cumulus_lambda_functions.lib.aws.aws_cognito import AwsCognito
from cumulus_lambda_functions.lib.aws.es_abstract import ESAbstract
from cumulus_lambda_functions.lib.aws.es_factory import ESFactory
from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants

LOGGER = logging.getLogger(__name__)


class UDSAuthorizorEsIdentityPool(UDSAuthorizorAbstract):

def __init__(self, user_pool_id: str) -> None:
super().__init__()
es_url = os.getenv('ES_URL') # TODO validation
self.__authorization_index = os.getenv('AUTHORIZATION_INDEX') # LDAP_Group_Permission
es_port = int(os.getenv('ES_PORT', '443'))
self.__cognito = AwsCognito(user_pool_id)
self.__es: ESAbstract = ESFactory().get_instance('AWS',
index=self.__authorization_index,
base_url=es_url,
port=es_port)

def add_authorized_group(self, action: [str], resource: [str], tenant: str, venue: str, ldap_group_name: str):
self.__es.index_one({
DBConstants.action_key: action,
DBConstants.resource_key: resource,
DBConstants.tenant: tenant,
DBConstants.tenant_venue: venue,
DBConstants.authorized_group_name_key: ldap_group_name,
}, f'{tenant}__{venue}__{ldap_group_name}', self.__authorization_index)
return

def delete_authorized_group(self, tenant: str, venue: str, ldap_group_name: str):
self.__es.delete_by_query({
'query': {
'bool': {
'must': [
{'term': {DBConstants.tenant: tenant}},
{'term': {DBConstants.tenant_venue: venue}},
{'term': {DBConstants.authorized_group_name_key: ldap_group_name}},
]
}
}
})
return

def list_authorized_groups_for(self, tenant: str, venue: str):
result = self.__es.query_pages({
'query': {
'bool': {
'must': [
{'term': {DBConstants.tenant: tenant}},
{'term': {DBConstants.tenant_venue: venue}},
]
}
},
'sort': [
{DBConstants.tenant: {'order': 'asc'}},
{DBConstants.tenant_venue: {'order': 'asc'}},
{DBConstants.authorized_group_name_key: {'order': 'asc'}},
]
})
result = [k['_source'] for k in result['hits']['hits']]
return result

def update_authorized_group(self, action: [str], resource: [str], tenant: str, venue: str, ldap_group_name: str):
self.__es.update_one({
DBConstants.action_key: action,
DBConstants.resource_key: resource,
DBConstants.tenant: tenant,
DBConstants.tenant_venue: venue,
DBConstants.authorized_group_name_key: ldap_group_name,
}, f'{tenant}__{venue}__{ldap_group_name}', self.__authorization_index)
return

def get_authorized_tenant(self, username: str, action: str, resource: str) -> list:
belonged_groups = set(self.__cognito.get_groups(username))

authorized_groups = self.__es.query({
'query': {
'bool': {
'must': [
{
'terms': {
DBConstants.authorized_group_name_key: list(belonged_groups),
}
},
{
'term': {
DBConstants.action_key: action,
}
},
{
'term': {
DBConstants.resource_key: resource,
}
}
]
}
}
})
return [k['_source'] for k in authorized_groups['hits']['hits']]

def authorize(self, username, resource, action) -> bool:
belonged_groups = set(self.__cognito.get_groups(username))
authorized_groups = self.__es.query({
'query': {
'match_all': {} # TODO
}
})
LOGGER.debug(f'belonged_groups for {username}: {belonged_groups}')
authorized_groups = set([k['_source']['group_name'] for k in authorized_groups['hits']['hits']])
LOGGER.debug(f'authorized_groups for {resource}-{action}: {authorized_groups}')
if any([k in authorized_groups for k in belonged_groups]):
LOGGER.debug(f'{username} is authorized for {resource}-{action}')
return True
LOGGER.debug(f'{username} is NOT authorized for {resource}-{action}')
return False
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from cumulus_lambda_functions.lib.aws.factory_abstract import FactoryAbstract


class UDSAuthorizerFactory(FactoryAbstract):
cognito = 'COGNITO'

def get_instance(self, class_type, **kwargs):
if class_type == self.cognito:
from cumulus_lambda_functions.lib.authorization.uds_authorizer_es_identity_pool import \
UDSAuthorizorEsIdentityPool
return UDSAuthorizorEsIdentityPool(kwargs['user_pool_id'])
raise ValueError(f'class_type: {class_type} not implemented')
20 changes: 20 additions & 0 deletions cumulus_lambda_functions/lib/aws/aws_cognito.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from cumulus_lambda_functions.lib.aws.aws_cred import AwsCred


class AwsCognito(AwsCred):
def __init__(self, user_pool_id: str):
super().__init__()
self.__cognito = self.get_client('cognito-idp')
self.__user_pool_id = user_pool_id

def get_groups(self, username: str):
response = self.__cognito.admin_list_groups_for_user(
Username=username,
UserPoolId=self.__user_pool_id,
Limit=60,
# NextToken='string'
)
if response is None or 'Groups' not in response:
return []
belonged_groups = [k['GroupName'] for k in response['Groups']]
return belonged_groups
16 changes: 16 additions & 0 deletions cumulus_lambda_functions/lib/aws/aws_cred.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ def __init__(self):
else:
LOGGER.debug('using default session as there is no aws_access_key_id')

@property
def region(self):
return self.__region

@region.setter
def region(self, val):
"""
:param val:
:return: None
"""
self.__region = val
return

@property
def boto3_session(self):
return self.__boto3_session
Expand All @@ -56,6 +69,9 @@ def boto3_session(self, val):
self.__boto3_session = val
return

def get_session(self):
return boto3.Session(**self.boto3_session)

def get_resource(self, service_name: str):
return boto3.Session(**self.boto3_session).resource(service_name)

Expand Down
Loading

0 comments on commit 18e391c

Please sign in to comment.