Skip to content
Open
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
175 changes: 26 additions & 149 deletions cumulus_lambda_functions/uds_api/collections_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections

from cumulus_lambda_functions.uds_api.fast_api_utils import FastApiUtils
from cumulus_lambda_functions.uds_api.fast_api_utils import FastApiUtils, uds_api_authorize, api_authorized_collections

from cumulus_lambda_functions.lib.authorization.uds_authorizer_factory import UDSAuthorizerFactory

from cumulus_lambda_functions.lib.authorization.uds_authorizer_abstract import UDSAuthorizorAbstract

from cumulus_lambda_functions.lib.lambda_logger_generator import LambdaLoggerGenerator
from fastapi import APIRouter, HTTPException, Request, Response
from fastapi import APIRouter, HTTPException, Request, Response, Depends

from cumulus_lambda_functions.uds_api.dapa.collections_dapa_cnm import CnmRequestBody, CollectionsDapaCnm
from cumulus_lambda_functions.uds_api.dapa.collections_dapa_creation import CollectionDapaCreation, \
Expand All @@ -35,37 +35,15 @@
responses={404: {"description": "Not found"}},
)

@router.put("")
@router.put("/")
@router.put("/{collection_id}", dependencies=[Depends(uds_api_authorize)])
@router.put("/{collection_id}/", dependencies=[Depends(uds_api_authorize)])
async def ingest_cnm_dapa(request: Request, new_cnm_body: CnmRequestBody, response: Response, response_class=JSONResponse):
"""
Ingestion of Granules for a given collection via CNM

This is a facade endpoint which will trigger another endpoint which takes some time to execute ingestion
"""
LOGGER.debug(f'starting ingest_cnm_dapa')
collection_id = new_cnm_body.model_dump()
if 'features' not in collection_id or len(collection_id['features']) < 1 or 'collection' not in collection_id['features'][0]:
raise HTTPException(status_code=500, detail=json.dumps({
'message': 'missing collection_id in request_body["features"][0]["collection"]'
}))
collection_id = collection_id['features'][0]['collection']
collection_id = collection_id.split('___')[0] # split id, version and only keeping id. TODO need this?
authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \
.get_instance(UDSAuthorizerFactory.cognito,
es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443'))
)
auth_info = FastApiUtils.get_authorization_info(request)
collection_identifier = UdsCollections.decode_identifier(collection_id)
if not authorizer.is_authorized_for_collection(DBConstants.create, collection_id,
auth_info['ldap_groups'],
collection_identifier.tenant,
collection_identifier.venue):
LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}')
raise HTTPException(status_code=403, detail=json.dumps({
'message': 'not authorized to execute this action'
}))
try:
cnm_prep_result = CollectionsDapaCnm(new_cnm_body.model_dump()).start_facade(request.url)
except Exception as e:
Expand All @@ -77,8 +55,8 @@ async def ingest_cnm_dapa(request: Request, new_cnm_body: CnmRequestBody, respon
raise HTTPException(status_code=cnm_prep_result['statusCode'], detail=cnm_prep_result['body'])


@router.put("/actual")
@router.put("/actual/")
@router.put("/{collection_id}/actual", dependencies=[Depends(uds_api_authorize)])
@router.put("/{collection_id}/actual/", dependencies=[Depends(uds_api_authorize)])
async def ingest_cnm_dapa_actual(request: Request, new_cnm_body: CnmRequestBody, response_class=JSONResponse):
"""
Real ingestion of Granules for a given collection via CNM
Expand All @@ -98,37 +76,15 @@ async def ingest_cnm_dapa_actual(request: Request, new_cnm_body: CnmRequestBody,
raise HTTPException(status_code=cnm_result['statusCode'], detail=cnm_result['body'])


@router.post("")
@router.post("/")
@router.post("/{collection_id}", dependencies=[Depends(uds_api_authorize)])
@router.post("/{collection_id}/", dependencies=[Depends(uds_api_authorize)])
async def create_new_collection(request: Request, new_collection: CumulusCollectionModel, response: Response):
"""
Creating a new Cumulus Collection

This is a facade endpoint which will trigger another endpoint which takes some time to hit Cumulus collection creation endpoint.
"""
LOGGER.debug(f'starting create_new_collection')
new_collection = new_collection.model_dump()
collection_id = new_collection
if 'id' not in collection_id:
raise HTTPException(status_code=500, detail=json.dumps({
'message': 'missing collection_id in request_body["id"]'
}))
collection_id = new_collection['id']
authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \
.get_instance(UDSAuthorizerFactory.cognito,
es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443'))
)
auth_info = FastApiUtils.get_authorization_info(request)
collection_identifier = UdsCollections.decode_identifier(collection_id)
if not authorizer.is_authorized_for_collection(DBConstants.create, collection_id,
auth_info['ldap_groups'],
collection_identifier.tenant,
collection_identifier.venue):
LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}')
raise HTTPException(status_code=403, detail=json.dumps({
'message': 'not authorized to execute this action'
}))
try:
# new_collection = request.body()
bearer_token = request.headers.get('Authorization', '')
Expand All @@ -143,36 +99,13 @@ async def create_new_collection(request: Request, new_collection: CumulusCollect
raise HTTPException(status_code=creation_result['statusCode'], detail=creation_result['body'])


@router.post("/actual")
@router.post("/actual/")
async def create_new_collection_real(request: Request, new_collection: CumulusCollectionModel):
@router.post("/{collection_id}/actual", dependencies=[Depends(uds_api_authorize)])
@router.post("/{collection_id}/actual/", dependencies=[Depends(uds_api_authorize)])
async def create_new_collection_real(request: Request, collection_id: str, new_collection: CumulusCollectionModel):
"""
Actual endpoint to create a new Cumulus Collection
"""
LOGGER.debug(f'starting create_new_collection_real')
new_collection = new_collection.model_dump()
collection_id = new_collection
if 'id' not in collection_id:
raise HTTPException(status_code=500, detail=json.dumps({
'message': 'missing collection_id in request_body["id"]'
}))
collection_id = new_collection['id']
authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \
.get_instance(UDSAuthorizerFactory.cognito,
es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443'))
)
auth_info = FastApiUtils.get_authorization_info(request)
collection_identifier = UdsCollections.decode_identifier(collection_id)
if not authorizer.is_authorized_for_collection(DBConstants.create, collection_id,
auth_info['ldap_groups'],
collection_identifier.tenant,
collection_identifier.venue):
LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}')
raise HTTPException(status_code=403, detail=json.dumps({
'message': 'not authorized to execute this action'
}))

try:
creation_result = CollectionDapaCreation(new_collection).create()
except Exception as e:
Expand All @@ -182,32 +115,11 @@ async def create_new_collection_real(request: Request, new_collection: CumulusCo
return creation_result['body'], creation_result['statusCode']
raise HTTPException(status_code=creation_result['statusCode'], detail=creation_result['body'])

@router.get("/{collection_id}")
@router.get("/{collection_id}/")
@router.get("/{collection_id}", dependencies=[Depends(uds_api_authorize)])
@router.get("/{collection_id}/, dependencies=[Depends(uds_api_authorize)]")
async def get_single_collection(request: Request, collection_id: str, limit: Union[int, None] = 10, offset: Union[int, None] = 0, ):
LOGGER.debug(f'starting get_single_collection: {collection_id}')
LOGGER.debug(f'starting get_single_collection request: {request}')

authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \
.get_instance(UDSAuthorizerFactory.cognito,
es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443'))
)
auth_info = FastApiUtils.get_authorization_info(request)
uds_collections = UdsCollections(es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443')), es_type=os.getenv('ES_TYPE', 'AWS'))
if collection_id is None or collection_id == '':
raise HTTPException(status_code=500, detail=f'missing or invalid collection_id: {collection_id}')
collection_identifier = uds_collections.decode_identifier(collection_id)
if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id,
auth_info['ldap_groups'],
collection_identifier.tenant,
collection_identifier.venue):
LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}')
raise HTTPException(status_code=403, detail=json.dumps({
'message': 'not authorized to execute this action'
}))

try:
custom_params = {}
if limit > CollectionDapaQuery.max_limit:
Expand All @@ -229,36 +141,21 @@ async def get_single_collection(request: Request, collection_id: str, limit: Uni

@router.get("")
@router.get("/")
async def query_collections(request: Request, collection_id: Union[str, None] = None, limit: Union[int, None] = 10, offset: Union[int, None] = 0, ):
async def query_collections(request: Request, collection_id: Union[str, None] = None, limit: Union[int, None] = 10, offset: Union[int, None] = 0, authorized_collections = Depends(api_authorized_collections)):
LOGGER.debug(f'starting query_collections: {collection_id}')
LOGGER.debug(f'starting query_collections request: {request}')

authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \
.get_instance(UDSAuthorizerFactory.cognito,
es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443'))
)
auth_info = FastApiUtils.get_authorization_info(request)
uds_collections = UdsCollections(es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443')), es_type=os.getenv('ES_TYPE', 'AWS'))
if collection_id is not None:
collection_identifier = uds_collections.decode_identifier(collection_id)
if not authorizer.is_authorized_for_collection(DBConstants.read, collection_id,
auth_info['ldap_groups'],
collection_identifier.tenant,
collection_identifier.venue):
LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}')
raise HTTPException(status_code=403, detail=json.dumps({
'message': 'not authorized to execute this action'
}))
else:
collection_regexes = authorizer.get_authorized_collections(DBConstants.read, auth_info['ldap_groups'])
LOGGER.info(f'collection_regexes: {collection_regexes}')
authorized_collections = uds_collections.get_collections(collection_regexes)
LOGGER.info(f'authorized_collections: {authorized_collections}')
collection_id = [k[DBConstants.collection_id] for k in authorized_collections]
LOGGER.info(f'authorized_collection_ids: {collection_id}')
# NOTE: 2022-11-21: only pass collections. not versions
raise HTTPException(status_code=301, detail=f'Pls use collection_id as path parameter to retrieve a single collection.')

# collection_regexes = authorizer.get_authorized_collections(DBConstants.read, auth_info['ldap_groups'])
LOGGER.info(f'collection_regexes: {authorized_collections}')
authorized_collections = uds_collections.get_collections(collection_regexes)
LOGGER.info(f'authorized_collections: {authorized_collections}')
collection_id = [k[DBConstants.collection_id] for k in authorized_collections]
LOGGER.info(f'authorized_collection_ids: {collection_id}')
# NOTE: 2022-11-21: only pass collections. not versions

try:
custom_params = {}
Expand All @@ -279,32 +176,12 @@ async def query_collections(request: Request, collection_id: Union[str, None] =
return collections_result['body']
raise HTTPException(status_code=collections_result['statusCode'], detail=collections_result['body'])

@router.delete("/{collection_id}")
@router.delete("/{collection_id}/")
@router.delete("/{collection_id}", dependencies=[Depends(uds_api_authorize)])
@router.delete("/{collection_id}/", dependencies=[Depends(uds_api_authorize)])
async def delete_single_collection(request: Request, collection_id: str):
LOGGER.debug(f'starting delete_single_collection: {collection_id}')
LOGGER.debug(f'starting delete_single_collection request: {request}')

authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \
.get_instance(UDSAuthorizerFactory.cognito,
es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443'))
)
auth_info = FastApiUtils.get_authorization_info(request)
uds_collections = UdsCollections(es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443')), es_type=os.getenv('ES_TYPE', 'AWS'))
if collection_id is None or collection_id == '':
raise HTTPException(status_code=500, detail=f'missing or invalid collection_id: {collection_id}')
collection_identifier = uds_collections.decode_identifier(collection_id)
if not authorizer.is_authorized_for_collection(DBConstants.delete, collection_id,
auth_info['ldap_groups'],
collection_identifier.tenant,
collection_identifier.venue):
LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}')
raise HTTPException(status_code=403, detail=json.dumps({
'message': 'not authorized to execute this action'
}))

collection_identifier = UdsCollections.decode_identifier(collection_id)
granules_count = GranulesDbIndex().get_size(collection_identifier.tenant, collection_identifier.venue,
collection_id)
LOGGER.debug(f'granules_count: {granules_count} for {collection_id}')
Expand Down
55 changes: 55 additions & 0 deletions cumulus_lambda_functions/uds_api/fast_api_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@

from mdps_ds_lib.lib.constants import Constants

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 fastapi import APIRouter, HTTPException, Request, Response

from cumulus_lambda_functions.lib.uds_db.db_constants import DBConstants
from cumulus_lambda_functions.lib.uds_db.uds_collections import UdsCollections
from cumulus_lambda_functions.uds_api.web_service_constants import WebServiceConstants

LOGGER = LambdaLoggerGenerator.get_logger(__name__, LambdaLoggerGenerator.get_level_from_env())
Expand All @@ -20,6 +24,7 @@ def get_authorization_info(request: Request):
"""
:return:
"""
# TODO this can be a factory method
action = request.method
resource = request.url.path
bearer_token = request.headers.get('Authorization', '')
Expand Down Expand Up @@ -90,3 +95,53 @@ def prep_stac_browser():
FastApiUtils.replace_in_folder(temp_static_parent_dir, f'"{WebServiceConstants.SETTING_PLACEHOLDER}"', json.dumps(stac_browser_settings))
FastApiUtils.replace_in_folder(temp_static_parent_dir, f"'{WebServiceConstants.SETTING_PLACEHOLDER}'", json.dumps(stac_browser_settings))
return stac_browser_prefix, temp_static_parent_dir


async def api_authorized_collections(request: Request):
auth_info = FastApiUtils.get_authorization_info(request) # TODO how to get different authorization info?

authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \
.get_instance(os.getenv('AUTHORIZER_TYPE'),
es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443')),
use_ssl=os.getenv('ES_USE_SSL', 'TRUE').strip() is True,
)
collection_regexes = authorizer.get_authorized_collections(DBConstants.read, auth_info['ldap_groups'])
return collection_regexes

async def uds_api_authorize(request: Request):
db_constants_map = {
'GET': DBConstants.read,
'DELETE': DBConstants.delete,
'PUT': DBConstants.create,
'POST': DBConstants.create,
'PATCH': DBConstants.create, # TODO Update?
}
authorization_type = db_constants_map[request.method]
if request.method not in db_constants_map:
raise HTTPException(status_code=405, detail=json.dumps({
'message': f'unknown HTTP method for authorization. Pls use these {db_constants_map}'
}))
if 'collection_id' not in request.path_params:
raise HTTPException(status_code=400, detail=json.dumps({
'message': 'collection_id is needed as path parameter for authorization.'
}))
collection_id = request.path_params['collection_id']
auth_info = FastApiUtils.get_authorization_info(request) # TODO how to get different authorization info?

authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \
.get_instance(os.getenv('AUTHORIZER_TYPE'),
es_url=os.getenv('ES_URL'),
es_port=int(os.getenv('ES_PORT', '443')),
use_ssl=os.getenv('ES_USE_SSL', 'TRUE').strip() is True,
)
collection_identifier = UdsCollections.decode_identifier(collection_id)
if not authorizer.is_authorized_for_collection(authorization_type, collection_id,
auth_info['ldap_groups'],
collection_identifier.tenant,
collection_identifier.venue):
LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}')
raise HTTPException(status_code=403, detail=json.dumps({
'message': 'not authorized to execute this action'
}))
return True
Loading
Loading