"""Blueprint for account API.""" from abacus_common_logic.utils.authorization import permissions_authorize_many_accounts from abacus_common_logic.views.create_view import CreateView from abacus_common_logic.views.item_view import ItemView from abacus_common_logic.views.list_view import ListView from flask import Blueprint from flask import g from flask import request from owsrequest import flask_request from owsresponse import response from owsresponse.adaptors.flask import flaskify from abacus_account.config import ows_client from abacus_account.constants import error from abacus_account.logic import account as logic from abacus_account.logic.account_dataload import dataload_accounts_by_ids from abacus_account.logic.account_search import get_accounts from abacus_account.logic.account_search import get_accounts_by_ids from abacus_account.models.account import Account from abacus_account.schemas.account import AccountDetailSchema from abacus_account.schemas.account import AccountPostSchema from abacus_account.schemas.account import AccountPutSchema from abacus_account.utils.authorization import authorize_many_accounts from abacus_account.utils.format_error import validation_error from abacus_account.utils.request import get_optional_numeric_list_from_params account_api = Blueprint('account_api', __name__) class AccountItemView(ItemView): """View for finding an account by ID.""" model_class = Account object_detail_schema = AccountDetailSchema() put_schema = AccountPutSchema() def get(self, object_id, **kwargs): """Get account by id.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: authorized = authorize_many_accounts([object_id]) if not authorized: return flaskify(response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=403, )) authorized = permissions_authorize_many_accounts( ows_client, g.request_context.profile_type, g.request_context.profile_id, [object_id] ) if not authorized: return flaskify(response.create_error_response( code=error.ERROR_CODE_FORBIDDEN, message=error.ERROR_MESSAGE_FORBIDDEN_USER, status=403 )) return super().get(object_id, **kwargs) def put(self, object_id, **kwargs): """Update an account.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify(response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, )) return super().put(object_id, **kwargs) def update_handler(self, obj, **params): """Update an account.""" return logic.update_account(obj, **params) class AccountCreateView(CreateView): """Handles account creation.""" post_schema = AccountPostSchema() def post(self, **kwargs): """Create an account.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify(response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, )) return super().post(**kwargs) def create_handler(self, **params): """Create an account.""" return logic.create_account(**params) class AccountList(ListView): """View for listing accounts.""" model_class = Account list_entry_schema = AccountDetailSchema() def get(self): """GET accounts.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify(response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, )) return flaskify(get_accounts(request.args)) def post(self): """Get accounts by ids. NOTE: This endpoint uses a POST method to allow for a larger list of query args. """ # first get the accounts based on the params account_list, total_count = get_accounts_by_ids(request.json) # then check if the user is authorized to access the accounts. # if the FF is off and the user is not authorized by using an # unsupported profile type, then that user will incur the # penalty of searching the accounts. # this is changing the behavior. before, calling this endpoint with # an invalid profile type would error with a 403, whereas now it # will respond with an empty list. access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: account_ids = [account.get('account_id') for account in account_list] if len(account_ids) > 0: authorized = authorize_many_accounts(account_ids) if not authorized: return flaskify(response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=403, )) return flaskify( response.Response({'items': account_list, 'total_count': total_count}) ) class AccountsDataloader(ListView): """View for accounts dataloader.""" model_class = Account list_entry_schema = AccountDetailSchema() def post(self): """Get accounts by ids. NOTE: This endpoint uses a POST method to allow for a larger list of query args. """ try: account_ids = get_optional_numeric_list_from_params() if not account_ids: return flaskify(validation_error(error.ERROR_INVALID_IDS.format( object='Account'))) except ValueError: return flaskify(validation_error(error.ERROR_INVALID_IDS.format( object='Account'))) access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: authorized = authorize_many_accounts(account_ids) if not authorized: return flaskify(response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=403, )) authorized = permissions_authorize_many_accounts( ows_client, g.request_context.profile_type, g.request_context.profile_id, account_ids ) if not authorized: return flaskify(response.create_error_response( code=error.ERROR_CODE_FORBIDDEN, message=error.ERROR_MESSAGE_FORBIDDEN_USER, status=403 )) return flaskify(dataload_accounts_by_ids(account_ids)) class EligibleAccountList(ListView): """View for eligible accounts.""" model_class = Account def get(self): """GET accounts.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify(response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, )) return flaskify(logic.get_eligible_accounts_for_group_id( request.args.get('payment_group_id') )) account_api.add_url_rule( '/account/', methods=['GET', 'PUT'], view_func=AccountItemView.as_view('account') ) account_api.add_url_rule( '/account', methods=['POST'], view_func=AccountCreateView.as_view('create_account') ) account_api.add_url_rule( '/account/dataloader', methods=['POST'], view_func=AccountsDataloader.as_view('accounts_dataloader') ) account_api.add_url_rule( '/accounts/', methods=['GET', 'POST'], view_func=AccountList.as_view('list_accounts') ) account_api.add_url_rule( '/eligible-accounts', view_func=EligibleAccountList.as_view('eligible_list_accounts')) @account_api.route( '/account//payment-eligibility-status/', methods=['GET'] ) def get_account_payment_eligibility_status(object_id): """Endpoint to GET account's payment eligibility status.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: authorized = authorize_many_accounts([object_id]) if not authorized: return flaskify(response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=403, )) authorized = permissions_authorize_many_accounts( ows_client, g.request_context.profile_type, g.request_context.profile_id, [object_id] ) if not authorized: return flaskify(response.create_error_response( code=error.ERROR_CODE_FORBIDDEN, message=error.ERROR_MESSAGE_FORBIDDEN_USER, status=403 )) return flaskify(logic.get_payment_eligibility_status(object_id)) @account_api.route( '/account//sap/', methods=['GET'] ) def get_sap_formatted_account_info(object_id): """Endpoint to GET SAP formatted account details.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify(response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, )) return flaskify(logic.get_sap_formatted_account_info(object_id))