def authorized( # pylint: disable=too-many-return-statements identifier: str, jwt: JwtManager, action: List[str]) -> bool: """Assert that the user is authorized to create filings against the business identifier.""" # if they are registry staff, they are always authorized if not action or not identifier or not jwt: return False if jwt.validate_roles([STAFF_ROLE]) \ or jwt.validate_roles([SYSTEM_ROLE]) \ or jwt.validate_roles([COLIN_SVC_ROLE]): return True if jwt.has_one_of_roles([BASIC_USER, PUBLIC_USER]): # if the action is create_comment or courtOrder/registrarsNotation/registrarsOrder filings # disallow - only staff are allowed staff_only_actions = [ 'add_comment', 'court_order', 'registrars_notation', 'registrars_order' ] if any(elem in action for elem in staff_only_actions): return False template_url = current_app.config.get('AUTH_SVC_URL') auth_url = template_url.format(**vars()) token = jwt.get_token_auth_header() headers = {'Authorization': 'Bearer ' + token} try: http = Session() retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504]) http.mount('http://', HTTPAdapter(max_retries=retries)) rv = http.get(url=auth_url, headers=headers) if rv.status_code != HTTPStatus.OK \ or not rv.json().get('roles'): return False if all(elem.lower() in rv.json().get('roles') for elem in action): return True except ( exceptions.ConnectionError, # pylint: disable=broad-except exceptions.Timeout, ValueError, Exception) as err: current_app.logger.error( f'template_url {template_url}, svc:{auth_url}') current_app.logger.error( f'Authorization connection failure for {identifier}, using svc:{auth_url}', err) return False return False
def jwt(app): def get_roles(a_dict): return a_dict['realm_access']['roles'] app.config['JWT_ROLE_CALLBACK'] = get_roles jwt = JwtManager() jwt.init_app(app) return jwt
def _create_invoice(business: Business, filing: Filing, filing_types: list, user_jwt: JwtManager) \ -> Tuple[int, dict, int]: """Create the invoice for the filing submission. Returns: { int: the paymentToken (id), or None dict: a dict of errors, or None int: the HTTPStatus error code, or None } """ payment_svc_url = current_app.config.get('PAYMENT_SVC_URL') mailing_address = business.mailing_address.one_or_none() payload = { 'paymentInfo': {'methodOfPayment': 'CC'}, 'businessInfo': { 'businessIdentifier': f'{business.identifier}', 'corpType': f'{business.identifier[:-7]}', 'businessName': f'{business.legal_name}', 'contactInfo': {'city': mailing_address.city, 'postalCode': mailing_address.postal_code, 'province': mailing_address.region, 'addressLine1': mailing_address.street, 'country': mailing_address.country} }, 'filingInfo': { 'filingTypes': filing_types } } if user_jwt.validate_roles([STAFF_ROLE]): routing_slip_number = get_str(filing.filing_json, 'filing/header/routingSlipNumber') if routing_slip_number: payload['accountInfo'] = {'routingSlip': routing_slip_number} try: token = user_jwt.get_token_auth_header() headers = {'Authorization': 'Bearer ' + token} rv = requests.post(url=payment_svc_url, json=payload, headers=headers, timeout=20.0) except (exceptions.ConnectionError, exceptions.Timeout) as err: current_app.logger.error(f'Payment connection failure for {business.identifier}: filing:{filing.id}', err) return {'message': 'unable to create invoice for payment.'}, HTTPStatus.PAYMENT_REQUIRED if rv.status_code == HTTPStatus.OK or rv.status_code == HTTPStatus.CREATED: pid = rv.json().get('id') filing.payment_token = pid filing.save() return None, None return {'message': 'unable to create invoice for payment.'}, HTTPStatus.PAYMENT_REQUIRED
def authorized(identifier: str, jwt: JwtManager) -> bool: # pylint: disable=too-many-return-statements """Verify the user is authorized to submit the request by inspecting the web token. The gateway has already verified the JWT with the OIDC service. """ if not jwt: return False # Could call the auth api here to check the token roles (/api/v1/orgs/{account_id}/authorizations), # but JWTManager.validate_roles does the same thing. # All users including staff must have the PPR role. if not jwt.validate_roles([PPR_ROLE]): return False # Account ID (idenfifier) is required if not staff. if identifier and identifier.strip() != '': return True # Remove when all staff changes made. if jwt.validate_roles([STAFF_ROLE]): return True # template_url = current_app.config.get('AUTH_SVC_URL') # auth_url = template_url.format(**vars()) # token = jwt.get_token_auth_header() # headers = {'Authorization': 'Bearer ' + token} # try: # http = Session() # retries = Retry(total=5, # backoff_factor=0.1, # status_forcelist=[500, 502, 503, 504]) # http.mount('http://', HTTPAdapter(max_retries=retries)) # rv = http.get(url=auth_url, headers=headers) # if rv.status_code != HTTPStatus.OK \ # or not rv.json().get('roles'): # return False # if all(elem.lower() in rv.json().get('roles') for elem in action): # return True # except (exceptions.ConnectionError, # pylint: disable=broad-except # exceptions.Timeout, # ValueError, # Exception) as err: # current_app.logger.error(f'template_url {template_url}, svc:{auth_url}') # current_app.logger.error(f'Authorization connection failure for {identifier}, using svc:{auth_url}', err) # return False return False
def has_roles(jwt: JwtManager, roles: List[str]) -> bool: """Assert the users JWT has the required role(s). Assumes the JWT is already validated. """ if jwt.validate_roles(roles): return True return False
def authorized(identifier: str, jwt: JwtManager) -> bool: """Assert that the user is authorized to create filings against the business identifier.""" # if they are registry staff, they are always authorized if jwt.validate_roles([STAFF_ROLE]): return True if jwt.validate_roles([COLIN_SVC_ROLE]): return True token = g.jwt_oidc_token_info username = token.get('username', None) if username and jwt.validate_roles( [BASIC_USER]) and identifier.upper() == username.upper(): return True return False
def is_staff(jwt: JwtManager) -> bool: # pylint: disable=too-many-return-statements """Return True if the user has the BC Registries staff role.""" if not jwt: return False if jwt.validate_roles([STAFF_ROLE]): return True return False
def authorized_token( # pylint: disable=too-many-return-statements identifier: str, jwt: JwtManager, action: List[str]) -> bool: """Assert that the user is authorized to submit API requests for a particular action.""" if not action or not identifier or not jwt: return False # All users including staff must have the PPR role. if not jwt.validate_roles([PPR_ROLE]): return False if jwt.has_one_of_roles([BASIC_USER, PRO_DATA_USER]): template_url = current_app.config.get('AUTH_SVC_URL') auth_url = template_url.format(**vars()) token = jwt.get_token_auth_header() headers = {'Authorization': 'Bearer ' + token} try: http = Session() retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504]) http.mount('http://', HTTPAdapter(max_retries=retries)) rv = http.get(url=auth_url, headers=headers) if rv.status_code != HTTPStatus.OK \ or not rv.json().get('roles'): return False if all(elem.lower() in rv.json().get('roles') for elem in action): return True except ( exceptions.ConnectionError, # pylint: disable=broad-except exceptions.Timeout, ValueError, Exception) as err: current_app.logger.error( f'template_url {template_url}, svc:{auth_url}') current_app.logger.error( f'Authorization connection failure for {identifier}, using svc:{auth_url}', err) return False return False
def check_auth(business_identifier: str, jwt: JwtManager, **kwargs): """Authorize the user for the business entity.""" bearer_token = jwt.get_token_auth_header() if jwt else None auth_url = current_app.config.get( 'AUTH_API_ENDPOINT') + f'entities/{business_identifier}/authorizations' auth_response = RestService.get(auth_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON) is_authorized: bool = False if auth_response: roles: list = auth_response.json().get('roles', []) if kwargs.get('one_of_roles', None): is_authorized = list(set(kwargs.get('one_of_roles')) & set(roles)) != [] if kwargs.get('contains_role', None): is_authorized = kwargs.get('contains_role') in roles if not is_authorized: abort(403)
This module is the API for the Names Examination system TODO: Fill in a larger description once the API is defined for V1 """ import config from namex.utils.logging import setup_logging setup_logging() ## important to do this first import os from .VERSION import __version__ from flask import Flask from namex.utils.run_version import get_run_version from flask_jwt_oidc import JwtManager jwt = JwtManager() from namex.services.nro import NROServices nro = NROServices() from namex.models import db, ma from namex.resources import api from namex import models run_version = get_run_version() def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): app = Flask(__name__) app.config.from_object(config.CONFIGURATION[run_mode])
# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an 'AS IS' BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Bring in the common JWT Manager.""" from functools import wraps from http import HTTPStatus from flask import g, request from flask_jwt_oidc import JwtManager from jose import jwt as josejwt from request_api.utils.enums import MinistryTeamWithKeycloackGroup, ProcessingTeamWithKeycloackGroup, IAOTeamWithKeycloackGroup jwt = ( JwtManager() ) # pylint: disable=invalid-name; lower case name as used by convention in most Flask apps class Auth: """Extending JwtManager to include additional functionalities.""" @classmethod def require(cls, f): """Validate the Bearer Token.""" @jwt.requires_auth @wraps(f) def decorated(*args, **kwargs): g.authorization_header = request.headers.get("Authorization", None) g.token_info = g.jwt_oidc_token_info
def _create_invoice(business: Business, filing: Filing, user_jwt: JwtManager) \ -> Tuple[int, dict, int]: """Create the invoice for the filing submission. Returns: { int: the paymentToken (id), or None dict: a dict of errors, or None int: the HTTPStatus error code, or None } """ payment_svc_url = current_app.config.get('PAYMENT_SVC_URL') filing_types = [] for k in filing.filing_json['filing'].keys(): if Filing.FILINGS.get(k, None): filing_types.append( {'filing_type_code': Filing.FILINGS[k].get('code')}) mailing_address = business.mailing_address.one_or_none() payload = { 'payment_info': { 'method_of_payment': 'CC' }, 'business_info': { 'business_identifier': f'{business.identifier}', 'corp_type': f'{business.identifier[:-7]}', 'business_name': f'{business.legal_name}', 'contact_info': { 'city': mailing_address.city, 'postal_code': mailing_address.postal_code, 'province': mailing_address.region, 'address_line_1': mailing_address.street, 'country': mailing_address.country } }, 'filing_info': { 'filing_types': filing_types } } try: token = user_jwt.get_token_auth_header() headers = {'Authorization': 'Bearer ' + token} rv = requests.post(url=payment_svc_url, json=payload, headers=headers, timeout=5.0) except (exceptions.ConnectionError, exceptions.Timeout) as err: current_app.logger.error( f'Payment connection failure for {business.identifier}: filing:{filing.id}', err) return { 'message': 'unable to create invoice for payment.' }, HTTPStatus.PAYMENT_REQUIRED if rv.status_code == HTTPStatus.OK or rv.status_code == HTTPStatus.CREATED: pid = rv.json().get('id') filing.payment_token = pid filing.save() return None, None return { 'message': 'unable to create invoice for payment.' }, HTTPStatus.PAYMENT_REQUIRED
"""Bring in the common JWT Manager and helper functions.""" from functools import wraps from http import HTTPStatus from flask import g, request from flask_jwt_oidc import JwtManager from ..exceptions import BusinessException jwt = JwtManager() # pylint: disable=invalid-name; lower case name as used by convention in most Flask apps class Auth(): """Extending JwtManager to include additional functionalities.""" @classmethod def require(cls, f): """Validate the Bearer Token.""" @jwt.requires_auth @wraps(f) def decorated(*args, **kwargs): g.authorization_header = request.headers.get('Authorization', None) g.token_info = g.jwt_oidc_token_info return f(*args, **kwargs) return decorated @classmethod def has_one_of_roles(cls, roles): """Check that at least one of the realm roles are in the token.
def _create_invoice(business: Business, # pylint: disable=too-many-locals filing: Filing, filing_types: list, user_jwt: JwtManager, payment_account_id: str = None) \ -> Tuple[int, dict, int]: """Create the invoice for the filing submission. Returns: { int: the paymentToken (id), or None dict: a dict of errors, or None int: the HTTPStatus error code, or None } """ payment_svc_url = current_app.config.get('PAYMENT_SVC_URL') if filing.filing_type == Filing.FILINGS[ 'incorporationApplication'].get('name'): mailing_address = Address.create_address( filing.json['filing']['incorporationApplication']['offices'] ['registeredOffice']['mailingAddress']) corp_type = filing.json['filing']['business'].get( 'legalType', Business.LegalTypes.BCOMP.value) try: business.legal_name = filing.json['filing'][ 'incorporationApplication']['nameRequest']['legalName'] except KeyError: business.legal_name = business.identifier else: mailing_address = business.mailing_address.one_or_none() corp_type = business.legal_type if business.legal_type else \ filing.json['filing']['business'].get('legalType') payload = { 'businessInfo': { 'businessIdentifier': f'{business.identifier}', 'corpType': f'{corp_type}', 'businessName': f'{business.legal_name}', 'contactInfo': { 'city': mailing_address.city, 'postalCode': mailing_address.postal_code, 'province': mailing_address.region, 'addressLine1': mailing_address.street, 'country': mailing_address.country } }, 'filingInfo': { 'filingTypes': filing_types } } folio_number = filing.json['filing']['header'].get('folioNumber', None) if folio_number: payload['filingInfo']['folioNumber'] = folio_number if user_jwt.validate_roles([STAFF_ROLE]) or \ user_jwt.validate_roles([SYSTEM_ROLE]): account_info = {} routing_slip_number = get_str(filing.filing_json, 'filing/header/routingSlipNumber') if routing_slip_number: account_info['routingSlip'] = routing_slip_number bcol_account_number = get_str(filing.filing_json, 'filing/header/bcolAccountNumber') if bcol_account_number: account_info['bcolAccountNumber'] = bcol_account_number dat_number = get_str(filing.filing_json, 'filing/header/datNumber') if dat_number: account_info['datNumber'] = dat_number if account_info: payload['accountInfo'] = account_info try: token = user_jwt.get_token_auth_header() headers = { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' } rv = requests.post(url=payment_svc_url, json=payload, headers=headers, timeout=20.0) except (exceptions.ConnectionError, exceptions.Timeout) as err: current_app.logger.error( f'Payment connection failure for {business.identifier}: filing:{filing.id}', err) return { 'message': 'unable to create invoice for payment.' }, HTTPStatus.PAYMENT_REQUIRED if rv.status_code in (HTTPStatus.OK, HTTPStatus.CREATED): pid = rv.json().get('id') filing.payment_token = pid filing.payment_status_code = rv.json().get('statusCode', '') filing.payment_account = payment_account_id filing.save() return { 'isPaymentActionRequired': rv.json().get('isPaymentActionRequired', False) }, HTTPStatus.CREATED if rv.status_code == HTTPStatus.BAD_REQUEST: # Set payment error type used to retrieve error messages from pay-api error_type = rv.json().get('type') filing.payment_status_code = error_type filing.save() return { 'payment_error_type': error_type, 'message': rv.json().get('detail') }, HTTPStatus.PAYMENT_REQUIRED return { 'message': 'unable to create invoice for payment.' }, HTTPStatus.PAYMENT_REQUIRED
def create_receipt(payment_identifier: str, invoice_identifier: str, filing_data: Tuple[Dict[str, Any]], jwt: JwtManager = None, skip_auth_check: bool = False): """Create receipt.""" current_app.logger.debug('<create receipt initiated', payment_identifier, invoice_identifier) bearer_token = jwt.get_token_auth_header() if jwt else None receipt_dict = { 'templateVars': { 'lineItems': [], }, 'templateName': 'payment_receipt_coops', 'reportName': 'payment_receipt_coops' } template_vars = receipt_dict['templateVars'] template_vars['coopsName'] = filing_data.get('corpName') template_vars['filingDateTime'] = filing_data.get('filingDateTime') # inovice number not mandatory ;since only one invoice exist for a payment now if not invoice_identifier: invoice_data = Invoice.find_by_payment_identifier( payment_identifier, jwt=jwt, skip_auth_check=skip_auth_check).asdict() else: invoice_data = Invoice.find_by_id( invoice_identifier, payment_identifier, jwt=jwt, skip_auth_check=skip_auth_check).asdict() template_vars['incorporationNumber'] = invoice_data['created_by'] template_vars['paymentInvoiceNumber'] = invoice_data['invoice_number'] if 'receipts' not in invoice_data: raise BusinessException(Error.PAY999) template_vars['receiptNumber'] = invoice_data['receipts'][0][ 'receipt_number'] for line_item in invoice_data['line_items']: template_vars['lineItems'].append({ 'description': line_item['description'], 'filingFees': '{:.2f}'.format(line_item['total']) }) template_vars['lineItems'].append({ 'description': 'Total', 'filingFees': '{:.2f}'.format(invoice_data['total']) }) current_app.logger.debug('<OAuthService invoked from receipt.py', current_app.config.get('REPORT_API_BASE_URL')) pdf_response = OAuthService.post( current_app.config.get('REPORT_API_BASE_URL'), bearer_token, AuthHeaderType.BEARER, ContentType.JSON, receipt_dict) current_app.logger.debug('<OAuthService responded to receipt.py') return pdf_response
from flask import Flask from flask_jwt_oidc import JwtManager import requests import config from colin_api.models.filing import Filing from registry_schemas import validate from utils.logging import setup_logging setup_logging( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'logging.conf')) # important to do this first # lower case name as used by convention in most Flask apps jwt = JwtManager() # pylint: disable=invalid-name SENTRY_LOGGING = LoggingIntegration( event_level=logging.ERROR # send errors as events ) SET_EVENTS_MANUALLY = False def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.config.from_object(config.CONFIGURATION[run_mode]) # Configure Sentry if app.config.get('SENTRY_DSN', None): sentry_sdk.init(dsn=app.config.get('SENTRY_DSN'), integrations=[SENTRY_LOGGING])
# Copyright © 2019 Province of British Columbia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Auth.""" from flask_jwt_oidc import JwtManager jwtmanager = JwtManager()