def send_email(cls, token, recipients: str, html_body: str): # pylint:disable=unused-argument """Send the email asynchronously, using the given details.""" subject = 'Your BC Registries statement is available' current_app.logger.info(f'send_email to recipients: {recipients}') notify_url = current_app.config.get('NOTIFY_API_URL') + '/notify/' notify_body = { 'recipients': recipients, 'content': { 'subject': subject, 'body': html_body } } notify_response = OAuthService.post( notify_url, token=token, auth_header_type=AuthHeaderType.BEARER, content_type=ContentType.JSON, data=notify_body) current_app.logger.info('send_email notify_response') if notify_response: response_json = json.loads(notify_response.text) if response_json['notifyStatus']['code'] != 'FAILURE': return True return False
def _save_bank_details(cls, access_token, party_number: str, # pylint: disable=too-many-arguments account_number: str, site_number: str, payment_info: Dict[str, str]): """Save bank details to the site.""" current_app.logger.debug('<Creating CFS payment details ') site_payment_url = current_app.config.get( 'CFS_BASE_URL') + f'/cfs/parties/{party_number}/accs/{account_number}/sites/{site_number}/payment/' bank_number = str(payment_info.get('bankInstitutionNumber')) branch_number = str(payment_info.get('bankTransitNumber')) # bank account name should match legal name name = re.sub(r'[^a-zA-Z0-9]+', ' ', payment_info.get('bankAccountName', '')) payment_details: Dict[str, str] = { 'bank_account_name': name[:30], 'bank_number': f'{bank_number:0>4}', 'branch_number': f'{branch_number:0>5}', 'bank_account_number': str(payment_info.get('bankAccountNumber')), 'country_code': DEFAULT_COUNTRY, 'currency_code': DEFAULT_CURRENCY } site_payment_response = OAuthService.post(site_payment_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, payment_details).json() payment_details = { 'bank_account_number': payment_info.get('bankAccountNumber'), 'bank_number': bank_number, 'bank_branch_number': branch_number, 'payment_instrument_number': site_payment_response.get('payment_instrument_number') } current_app.logger.debug('>Creating CFS payment details') return payment_details
def _create_site(access_token, account, contact_info, receipt_method): """Create site in PayBC.""" current_app.logger.debug('<Creating site ') if not contact_info: contact_info = {} site_url = current_app.config.get('CFS_BASE_URL') + '/cfs/parties/{}/accs/{}/sites/' \ .format(account.get('party_number', None), account.get('account_number', None)) site: Dict[str, Any] = { 'site_name': 'Site 1', # Make it dynamic if we ever need multiple sites per account 'city': get_non_null_value(contact_info.get('city'), DEFAULT_CITY), 'address_line_1': get_non_null_value(contact_info.get('addressLine1'), DEFAULT_ADDRESS_LINE_1), 'postal_code': get_non_null_value(contact_info.get('postalCode'), DEFAULT_POSTAL_CODE).replace(' ', ''), 'province': get_non_null_value(contact_info.get('province'), DEFAULT_JURISDICTION), 'country': get_non_null_value(contact_info.get('country'), DEFAULT_COUNTRY), 'customer_site_id': '1', 'primary_bill_to': 'Y', 'customer_profile_class': CFS_CUSTOMER_PROFILE_CLASS } if receipt_method: site['receipt_method'] = receipt_method try: site_response = OAuthService.post(site_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, site).json() except HTTPError as e: # If the site creation fails with 400, query and return site if e.response.status_code == 400: site_response = \ OAuthService.get(site_url, access_token, AuthHeaderType.BEARER, ContentType.JSON).json().get( 'items')[0] else: raise e current_app.logger.debug('>Creating site ') return site_response
def _get_account_contact(cls, auth_token: str, auth_account_id: str): """Return account contact by calling auth API.""" get_contact_endpoint = current_app.config.get( 'AUTH_API_ENDPOINT') + f'orgs/{auth_account_id}/contacts' contact_response = OAuthService.get(get_contact_endpoint, auth_token, AuthHeaderType.BEARER, ContentType.JSON) return contact_response.json().get('contacts')[0]
def create_daily_reports(cls, date: str, **kwargs): """Create and return daily report for the day provided.""" routing_slips: List[RoutingSlipModel] = RoutingSlipModel.search( dict( dateFilter=dict( endDate=date, startDate=date, target='created_on' ) ), page=1, limit=0, return_all=True )[0] total: float = 0 no_of_cash: int = 0 no_of_cheque: int = 0 total_cash_usd: float = 0 total_cheque_usd: float = 0 total_cash_cad: float = 0 total_cheque_cad: float = 0 # TODO Only CAD supported now, so just add up the total. for routing_slip in routing_slips: total += float(routing_slip.total) if routing_slip.payment_account.payment_method == PaymentMethod.CASH.value: no_of_cash += 1 # TODO check if the payment is CAD or USD. total_cash_cad += float(routing_slip.total) if routing_slip.total_usd is not None: total_cash_usd += float(routing_slip.total_usd) else: no_of_cheque += 1 total_cheque_cad += float(routing_slip.total) if routing_slip.total_usd is not None: total_cheque_usd += float(routing_slip.total_usd) report_dict = dict( templateName='routing_slip_report', reportName=f'Routing-Slip-Daily-Report-{date}', templateVars=dict( day=date, reportDay=str(get_local_time(datetime.now())), total=total, numberOfCashReceipts=no_of_cash, numberOfChequeReceipts=no_of_cheque, totalCashInUsd=total_cash_usd, totalChequeInUsd=total_cheque_usd, totalCashInCad=total_cash_cad, totalChequeInCad=total_cheque_cad ) ) pdf_response = OAuthService.post(current_app.config.get('REPORT_API_BASE_URL'), kwargs['user'].bearer_token, AuthHeaderType.BEARER, ContentType.JSON, report_dict) return pdf_response, report_dict.get('reportName')
def _update_site(cls, cfs_account: CfsAccountModel, receipt_method: str): access_token = CFSService.get_token().json().get('access_token') pad_stop_payload = { 'receipt_method': receipt_method } cfs_base: str = current_app.config.get('CFS_BASE_URL') site_url = f'{cfs_base}/cfs/parties/{cfs_account.cfs_party}/accs/{cfs_account.cfs_account}/' \ f'sites/{cfs_account.cfs_site}/' site_update_response = OAuthService.post(site_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, pad_stop_payload, is_put=True) return site_update_response.json()
def _create_party(access_token: str = None, party_name: str = None): """Create a party record in PayBC.""" current_app.logger.debug('<Creating party Record') party_url = current_app.config.get('CFS_BASE_URL') + '/cfs/parties/' party: Dict[str, Any] = { 'customer_name': party_name } party_response = OAuthService.post(party_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, party) current_app.logger.debug('>Creating party Record') return party_response.json()
def get_fas_token(): """Generate oauth token for FAS client which will be used for all communication.""" current_app.logger.debug('<Getting FAS token') token_url = current_app.config.get('CFS_BASE_URL', None) + '/oauth/token' basic_auth_encoded = base64.b64encode( bytes(current_app.config.get('CFS_FAS_CLIENT_ID') + ':' + current_app.config.get('CFS_FAS_CLIENT_SECRET'), 'utf-8')).decode('utf-8') data = 'grant_type=client_credentials' token_response = OAuthService.post(token_url, basic_auth_encoded, AuthHeaderType.BASIC, ContentType.FORM_URL_ENCODED, data) current_app.logger.debug('>Getting FAS token') return token_response
def _create_paybc_account(access_token, party, is_fas: bool): """Create account record in PayBC.""" current_app.logger.debug('<Creating CFS account') account_url = current_app.config.get('CFS_BASE_URL') + f"/cfs/parties/{party.get('party_number', None)}/accs/" account: Dict[str, Any] = { 'account_description': current_app.config.get('CFS_ACCOUNT_DESCRIPTION'), 'customer_profile_class': CFS_FAS_CUSTOMER_PROFILE_CLASS if is_fas else CFS_CUSTOMER_PROFILE_CLASS } account_response = OAuthService.post(account_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, account) current_app.logger.debug('>Creating CFS account') return account_response.json()
def test_post(app): """Test Post.""" with app.app_context(): mock_get_token = patch('pay_api.services.oauth_service.requests.post') mock_get = mock_get_token.start() mock_get.return_value = Mock(status_code=201) mock_get.return_value.json.return_value = {} get_token_response = OAuthService.post('http://google.com/', '', AuthHeaderType.BEARER, ContentType.JSON, {}) mock_get_token.stop() assert get_token_response.json() == {}
def get_token(): issuer_url = current_app.config.get('JWT_OIDC_ISSUER') token_url = issuer_url + '/protocol/openid-connect/token' # https://sso-dev.pathfinder.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/token basic_auth_encoded = base64.b64encode( bytes(current_app.config.get('KEYCLOAK_SERVICE_ACCOUNT_ID') + ':' + current_app.config.get( 'KEYCLOAK_SERVICE_ACCOUNT_SECRET'), 'utf-8')).decode('utf-8') data = 'grant_type=client_credentials' token_response = OAuthService.post(token_url, basic_auth_encoded, AuthHeaderType.BASIC, ContentType.FORM_URL_ENCODED, data) token = token_response.json().get('access_token') return token
def validate_bank_account(bank_details: Tuple[Dict[str, Any]]) -> Dict[str, str]: """Validate bank details by invoking CFS validation Service.""" current_app.logger.debug('<Validating bank account details') validation_url = current_app.config.get('CFS_BASE_URL') + '/cfs/validatepayins/' bank_number = str(bank_details.get('bankInstitutionNumber', None)) branch_number = str(bank_details.get('bankTransitNumber', None)) bank_details: Dict[str, str] = { 'accountNumber': bank_details.get('bankAccountNumber', None), 'branchNumber': f'{branch_number:0>5}', 'bankNumber': f'{bank_number:0>4}', } try: access_token = CFSService.get_token().json().get('access_token') # raise_for_error should be false so that HTTPErrors are not thrown.PAYBC sends validation errors as 404 bank_validation_response_obj = OAuthService.post(validation_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, bank_details, raise_for_error=False) if bank_validation_response_obj.status_code in (HTTPStatus.OK.value, HTTPStatus.BAD_REQUEST.value): bank_validation_response = bank_validation_response_obj.json() validation_response = { 'bank_number': bank_validation_response.get('bank_number', None), 'bank_name': bank_validation_response.get('bank_number', None), 'branch_number': bank_validation_response.get('branch_number', None), 'transit_address': bank_validation_response.get('transit_address', None), 'account_number': bank_validation_response.get('account_number', None), 'is_valid': bank_validation_response.get('CAS-Returned-Messages', None) == 'VALID', 'status_code': HTTPStatus.OK.value, 'message': CFSService._transform_error_message( bank_validation_response.get('CAS-Returned-Messages')) } else: current_app.logger.debug('<Bank validation HTTP exception- {}', bank_validation_response_obj.text) validation_response = { 'status_code': bank_validation_response_obj.status_code, 'message': ['Bank validation service cant be reached'] } except ServiceUnavailableException as exc: # suppress all other errors current_app.logger.debug('<Bank validation ServiceUnavailableException exception- {}', exc.error) validation_response = { 'status_code': HTTPStatus.SERVICE_UNAVAILABLE.value, 'message': [str(exc.error)] } return validation_response
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)
def check_auth(business_identifier: str, **kwargs): """Authorize the user for the business entity.""" user: UserContext = kwargs['user'] is_authorized: bool = False if Role.SYSTEM.value in user.roles: is_authorized = bool(Role.EDITOR.value in user.roles) else: bearer_token = user.bearer_token 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) 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)
def test_post_with_connection_errors(app): """Test Get with errors.""" with app.app_context(): mock_get_token = patch('pay_api.services.oauth_service.requests.post') mock_get = mock_get_token.start() mock_get.side_effect = HTTPError() mock_get.return_value.json.return_value = {} with pytest.raises(HTTPError) as excinfo: OAuthService.post('http://google.com/', '', AuthHeaderType.BEARER, ContentType.JSON, {}) assert excinfo.type == HTTPError mock_get_token.stop() with patch('pay_api.services.oauth_service.requests.post', side_effect=ConnectionError('mocked error')): with pytest.raises(ServiceUnavailableException) as excinfo: OAuthService.post('http://google.com/', '', AuthHeaderType.BEARER, ContentType.JSON, {}) assert excinfo.type == ServiceUnavailableException with patch('pay_api.services.oauth_service.requests.post', side_effect=ConnectTimeout('mocked error')): with pytest.raises(ServiceUnavailableException) as excinfo: OAuthService.post('http://google.com/', '', AuthHeaderType.BEARER, ContentType.JSON, {}) assert excinfo.type == ServiceUnavailableException
def upgrade(): # ### commands auto generated by Alembic - please adjust! ### op.create_table( 'bcol_payment_account', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('bcol_user_id', sa.String(length=50), nullable=True), sa.Column('bcol_account_id', sa.String(length=50), nullable=True), sa.Column('account_id', sa.Integer(), nullable=True), sa.ForeignKeyConstraint( ['account_id'], ['payment_account.id'], ), sa.PrimaryKeyConstraint('id')) op.create_index(op.f('ix_bcol_payment_account_account_id'), 'bcol_payment_account', ['account_id'], unique=False) op.create_index(op.f('ix_bcol_payment_account_bcol_account_id'), 'bcol_payment_account', ['bcol_account_id'], unique=False) op.create_index(op.f('ix_bcol_payment_account_bcol_user_id'), 'bcol_payment_account', ['bcol_user_id'], unique=False) op.create_table( 'credit_payment_account', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('corp_number', sa.String(length=20), nullable=True), sa.Column('corp_type_code', sa.String(length=10), nullable=True), sa.Column('paybc_account', sa.String(length=50), nullable=True), sa.Column('paybc_party', sa.String(length=50), nullable=True), sa.Column('paybc_site', sa.String(length=50), nullable=True), sa.Column('account_id', sa.Integer(), nullable=True), sa.ForeignKeyConstraint( ['account_id'], ['payment_account.id'], ), sa.ForeignKeyConstraint( ['corp_type_code'], ['corp_type.code'], ), sa.PrimaryKeyConstraint('id')) op.create_index(op.f('ix_credit_payment_account_account_id'), 'credit_payment_account', ['account_id'], unique=False) op.create_index(op.f('ix_credit_payment_account_paybc_account'), 'credit_payment_account', ['paybc_account'], unique=False) op.create_table( 'internal_payment_account', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('corp_number', sa.String(length=20), nullable=True), sa.Column('corp_type_code', sa.String(length=10), nullable=True), sa.Column('account_id', sa.Integer(), nullable=True), sa.ForeignKeyConstraint( ['account_id'], ['payment_account.id'], ), sa.ForeignKeyConstraint( ['corp_type_code'], ['corp_type.code'], ), sa.PrimaryKeyConstraint('id')) op.create_index(op.f('ix_internal_payment_account_account_id'), 'internal_payment_account', ['account_id'], unique=False) op.add_column('invoice', sa.Column('bcol_account_id', sa.Integer(), nullable=True)) op.add_column('invoice', sa.Column('credit_account_id', sa.Integer(), nullable=True)) op.add_column( 'invoice', sa.Column('internal_account_id', sa.Integer(), nullable=True)) op.drop_constraint('invoice_account_id_fkey', 'invoice', type_='foreignkey') op.create_foreign_key(None, 'invoice', 'bcol_payment_account', ['bcol_account_id'], ['id']) op.create_foreign_key(None, 'invoice', 'credit_payment_account', ['credit_account_id'], ['id']) op.create_foreign_key(None, 'invoice', 'internal_payment_account', ['internal_account_id'], ['id']) op.add_column( 'invoice', sa.Column('business_identifier', sa.String(length=20), nullable=True)) op.add_column( 'invoice', sa.Column('corp_type_code', sa.String(length=10), nullable=True)) op.create_foreign_key(None, 'invoice', 'corp_type', ['corp_type_code'], ['code']) # Create temp variable to keep the payment accounts payment_accounts: [] = [] conn = op.get_bind() res = conn.execute( f"select id,corp_number,corp_type_code,payment_system_code,account_number,party_number,site_number from payment_account;" ) results = res.fetchall() for result in results: payment_accounts.append({ "id": result[0], "corp_number": result[1], "corp_type_code": result[2], "payment_system_code": result[3], "account_number": result[4], "party_number": result[5], "site_number": result[6] }) # Now drop the columns from payment_account table op.drop_index('ix_payment_account_account_number', table_name='payment_account') op.drop_index('ix_payment_account_bcol_account_id', table_name='payment_account') op.drop_index('ix_payment_account_bcol_user_id', table_name='payment_account') op.drop_constraint('payment_account_payment_system_code_fkey', 'payment_account', type_='foreignkey') op.drop_constraint('payment_account_corp_type_code_fkey', 'payment_account', type_='foreignkey') op.drop_column('payment_account', 'payment_system_code') op.drop_column('payment_account', 'corp_type_code') op.drop_column('payment_account', 'site_number') op.drop_column('payment_account', 'bcol_user_id') op.drop_column('payment_account', 'account_number') op.drop_column('payment_account', 'bcol_account_id') op.drop_column('payment_account', 'corp_number') op.drop_column('payment_account', 'party_number') # Delete all records from payment_account op.execute('delete from payment_account;') # Create admin token to call auth-api config = current_app.config token_url = config.get( 'JWT_OIDC_ISSUER' ) + '/protocol/openid-connect/token' # https://sso-dev.pathfinder.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/token basic_auth_encoded = base64.b64encode( bytes( config.get('KEYCLOAK_SERVICE_ACCOUNT_ID') + ':' + config.get('KEYCLOAK_SERVICE_ACCOUNT_SECRET'), 'utf-8')).decode('utf-8') data = 'grant_type=client_credentials' token_response = OAuthService.post(token_url, basic_auth_encoded, AuthHeaderType.BASIC, ContentType.FORM_URL_ENCODED, data) token = token_response.json().get('access_token') payment_account_table = table('payment_account', column('auth_account_id', String)) credit_payment_account_table = table('credit_payment_account', column('corp_number', String), column('corp_type_code', String), column('paybc_account', String), column('paybc_party', String), column('paybc_site', String), column('account_id', Integer)) internal_payment_account_table = table('internal_payment_account', column('corp_number', String), column('corp_type_code', String), column('account_id', Integer)) for payment_account in payment_accounts: corp_number = payment_account.get('corp_number') payment_system_code = payment_account.get('payment_system_code') corp_type_code = payment_account.get('corp_type_code') account_number = payment_account.get('account_number') party_number = payment_account.get('party_number') site_number = payment_account.get('site_number') id = payment_account.get('id') if corp_number: # Call Auth-API to get the auth_account id auth_search_url = config.get( 'AUTH_API_ENDPOINT') + 'orgs?affiliation=' + corp_number.upper( ) orgs_response = None try: orgs_response = OAuthService.get( auth_search_url, token, AuthHeaderType.BEARER, ContentType.JSON).json().get('orgs') except Exception as e: print( f'--- Error Occured while getting org for affiliation {corp_number}' ) if orgs_response and orgs_response[0]: auth_account_id = str(orgs_response[0].get('id')) else: auth_account_id = f'PASSCODE_ACCOUNT_{corp_number}' res = conn.execute( f"select id from payment_account where auth_account_id = '{auth_account_id}'" ) results = res.fetchall() credit_payment_account_id = None internal_payment_account_id = None if results and results[0]: payment_account_id = results[0][0] else: op.bulk_insert(payment_account_table, [{ "auth_account_id": auth_account_id }]) res = conn.execute( f"select id from payment_account where auth_account_id = '{auth_account_id}'" ) results = res.fetchall() payment_account_id = results[0][0] if payment_system_code == 'PAYBC': print(f'Creating credit account for {payment_account_id}') op.bulk_insert(credit_payment_account_table, [{ "corp_number": corp_number, "corp_type_code": corp_type_code, "paybc_account": account_number, "paybc_party": party_number, "paybc_site": site_number, "account_id": payment_account_id }]) res = conn.execute( f"select id from credit_payment_account where account_id = '{payment_account_id}' and corp_number='{corp_number}' and paybc_account='{account_number}'" ) results = res.fetchall() credit_payment_account_id = results[0][0] elif payment_system_code == 'INTERNAL': print( f'Creating internal payment account for {payment_account_id}' ) op.bulk_insert(internal_payment_account_table, [{ "corp_number": corp_number, "corp_type_code": corp_type_code, "account_id": payment_account_id }]) res = conn.execute( f"select id from internal_payment_account where account_id = '{payment_account_id}' and corp_number='{corp_number}' and corp_type_code='{corp_type_code}'" ) results = res.fetchall() internal_payment_account_id = results[0][0] print('Internal ', internal_payment_account_id) print('Credit ', credit_payment_account_id) if internal_payment_account_id: op.execute( f"update invoice set internal_account_id = '{internal_payment_account_id}', corp_type_code='{corp_type_code}', business_identifier='{corp_number}' where account_id = '{id}'" ) elif credit_payment_account_id: op.execute( f"update invoice set credit_account_id = '{credit_payment_account_id}', corp_type_code='{corp_type_code}', business_identifier='{corp_number}' where account_id = '{id}'" ) op.drop_column('invoice', 'account_id')
def update_failed_distributions(cls): # pylint:disable=too-many-locals """Update failed distributions. Steps: 1. Get all invoices with status UPDATE_REVENUE_ACCOUNT. 2. Find the completed invoice reference for the invoice. 3. Call the paybc GET service and check if there is any revenue not processed. 4. If yes, update the revenue details. 5. Update the invoice status as PAID and save. """ gl_updated_invoices = InvoiceModel.query.filter_by( invoice_status_code=InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value).all() current_app.logger.debug(f'Found {len(gl_updated_invoices)} invoices to update revenue details.') if len(gl_updated_invoices) > 0: # pylint:disable=too-many-nested-blocks access_token: str = cls._get_token().json().get('access_token') paybc_ref_number: str = current_app.config.get('PAYBC_DIRECT_PAY_REF_NUMBER') paybc_svc_base_url = current_app.config.get('PAYBC_DIRECT_PAY_BASE_URL') for gl_updated_invoice in gl_updated_invoices: payment: PaymentModel = PaymentModel.find_payment_for_invoice(gl_updated_invoice.id) # For now handle only GL updates for Direct Pay, more to come in future if payment.payment_method_code != PaymentMethod.DIRECT_PAY.value: cls._update_invoice_status(gl_updated_invoice, InvoiceStatus.PAID.value) else: active_reference = list( filter(lambda reference: (reference.status_code == InvoiceReferenceStatus.COMPLETED.value), gl_updated_invoice.references))[0] payment_url: str = \ f'{paybc_svc_base_url}/paybc/payment/{paybc_ref_number}/{active_reference.invoice_number}' payment_details: dict = cls.get_payment_details(payment_url, access_token) if payment_details and payment_details.get('paymentstatus') == STATUS_PAID: has_gl_completed: bool = True for revenue in payment_details.get('revenue'): if revenue.get('glstatus') in STATUS_NOT_PROCESSED: has_gl_completed = False if not has_gl_completed: post_revenue_payload = { 'revenue': [] } invoice: InvoiceService = InvoiceService.find_by_id(identifier=gl_updated_invoice.id, skip_auth_check=True) payment_line_items = PaymentLineItemModel.find_by_invoice_ids([invoice.id]) index: int = 0 for payment_line_item in payment_line_items: distribution_code = DistributionCodeModel.find_by_id( payment_line_item.fee_distribution_id) index = index + 1 post_revenue_payload['revenue'].append( cls.get_revenue_details(index, distribution_code, payment_line_item.total)) if payment_line_item.service_fees is not None and payment_line_item.service_fees > 0: index = index + 1 post_revenue_payload['revenue'].append( cls.get_revenue_details(index, distribution_code, payment_line_item.service_fees, is_service_fee=True)) OAuthService.post(payment_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, post_revenue_payload) cls._update_invoice_status(gl_updated_invoice, InvoiceStatus.PAID.value)
def get_payment_details(cls, payment_url: str, access_token: str): """Get the receipt details by calling PayBC web service.""" payment_response = OAuthService.get(payment_url, access_token, AuthHeaderType.BEARER, ContentType.JSON).json() return payment_response
def check_auth(business_identifier: str, account_id: str = None, corp_type_code: str = None, **kwargs): # pylint: disable=unused-argument, too-many-branches """Authorize the user for the business entity and return authorization response.""" user: UserContext = kwargs['user'] is_authorized: bool = False auth_response = None if not account_id: account_id = user.account_id call_auth_svc: bool = True if Role.SYSTEM.value in user.roles \ and user.product_code != 'BUSINESS': # Call auth only if it's business (entities) call_auth_svc = False is_authorized = bool(Role.EDITOR.value in user.roles) # Add account name as the service client name auth_response = {'account': {'id': user.user_name}} if call_auth_svc: bearer_token = user.bearer_token if account_id: auth_url = current_app.config.get('AUTH_API_ENDPOINT') + f'orgs/{account_id}' \ f'/authorizations?expanded=true' additional_headers = None if corp_type_code: additional_headers = {'Product-Code': corp_type_code} auth_response = RestService.get( auth_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON, additional_headers=additional_headers).json() roles: list = auth_response.get('roles', []) elif business_identifier: auth_url = current_app.config.get( 'AUTH_API_ENDPOINT' ) + f'entities/{business_identifier}/authorizations?expanded=true' auth_response = RestService.get(auth_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON).json() roles: list = auth_response.get('roles', []) g.user_permission = auth_response.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 # Check if premium flag is required if kwargs.get('is_premium', False) and auth_response['account'][ 'accountType'] != AccountType.PREMIUM.value: is_authorized = False # For staff users, if the account is coming as empty add stub data # (businesses which are not affiliated won't have account) if Role.STAFF.value in user.roles and not auth_response.get( 'account', None): auth_response['account'] = { 'id': f'PASSCODE_ACCOUNT_{business_identifier}' } if Role.SYSTEM.value in user.roles and bool( Role.EDITOR.value in user.roles): is_authorized = True if not is_authorized: abort(403) # IF auth response is empty (means a service account or a business with no account by staff) if not auth_response: if Role.SYSTEM.value in user.roles: # Call auth only if it's business (entities) # Add account name as the service client name auth_response = { 'account': { 'id': user.user_name, 'paymentInfo': { 'methodOfPayment': PaymentMethod.DIRECT_PAY.value } } } elif Role.STAFF.value in user.roles: auth_response = { 'account': { 'id': PaymentMethod.INTERNAL.value, 'paymentInfo': { 'methodOfPayment': PaymentMethod.INTERNAL.value } } } return auth_response
def check_auth(business_identifier: str, account_id: str = None, corp_type_code: str = None, **kwargs): # pylint: disable=unused-argument, too-many-branches """Authorize the user for the business entity and return authorization response.""" user: UserContext = kwargs['user'] is_authorized: bool = False auth_response = None if not account_id: account_id = user.account_id call_auth_svc: bool = True if Role.SYSTEM.value in user.roles \ and user.product_code != 'BUSINESS': # Call auth only if it's business (entities) call_auth_svc = False is_authorized = bool(Role.EDITOR.value in user.roles) if call_auth_svc: bearer_token = user.bearer_token if business_identifier: auth_url = current_app.config.get( 'AUTH_API_ENDPOINT' ) + f'entities/{business_identifier}/authorizations?expanded=true' auth_response = RestService.get(auth_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON).json() roles: list = auth_response.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 # For staff users, if the account is coming as empty add stub data # (businesses which are not affiliated won't have account) if Role.STAFF.value in user.roles and not auth_response.get( 'account', None): auth_response['account'] = { 'id': f'PASSCODE_ACCOUNT_{business_identifier}' } elif account_id: if corp_type_code: auth_url = current_app.config.get('AUTH_API_ENDPOINT') + f'accounts/{account_id}/' \ f'products/{corp_type_code}/authorizations?expanded=true' auth_response = RestService.get(auth_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON).json() roles: list = auth_response.get('roles', []) if roles: is_authorized = True else: # For activities not specific to a product auth_url = current_app.config.get( 'AUTH_API_ENDPOINT') + f'orgs/{account_id}' org_response = RestService.get(auth_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON).json() # TODO Create a similar response as in auth response auth_response = { 'orgMembership': '', 'account': { 'id': org_response.get('id', None), 'name': org_response.get('name', None), 'accountType': org_response.get('orgType', None), 'paymentPreference': {} } } if org_response.get('payment_settings', None): auth_response['account']['paymentPreference'] = { 'methodOfPayment': org_response['payment_settings'][0].get( 'preferredPayment', None), 'bcOnlineUserId': org_response['payment_settings'][0].get( 'bcolUserId', None), 'bcOnlineAccountId': org_response['payment_settings'][0].get( 'bcolAccountId', None) } is_authorized = True # Check if premium flag is required if kwargs.get('is_premium', False) and auth_response['account'][ 'accountType'] != AccountType.PREMIUM.value: is_authorized = False if Role.SYSTEM.value in user.roles and bool( Role.EDITOR.value in user.roles): is_authorized = True if not is_authorized: abort(403) return auth_response
def upgrade(): # ### commands auto generated by Alembic - please adjust! ### op.add_column( 'invoice', sa.Column('created_name', sa.String(length=100), nullable=True)) op.add_column( 'invoice', sa.Column('updated_name', sa.String(length=100), nullable=True)) op.add_column( 'payment', sa.Column('created_name', sa.String(length=100), nullable=True)) op.add_column( 'payment', sa.Column('updated_name', sa.String(length=100), nullable=True)) # Check if there are records in payment or invoice table to update names conn = op.get_bind() res = conn.execute(f"select id from payment;") pay_results = res.fetchall() res = conn.execute(f"select id from invoice;") inv_results = res.fetchall() if len(pay_results) > 0 or len(inv_results) > 0: # Now call Keycloak API and update the names for existing record config = current_app.config issuer_url = config.get('JWT_OIDC_ISSUER') token_url = issuer_url + '/protocol/openid-connect/token' # https://sso-dev.pathfinder.gov.bc.ca/auth/realms/fcf0kpqr/protocol/openid-connect/token basic_auth_encoded = base64.b64encode( bytes( config.get('KEYCLOAK_SERVICE_ACCOUNT_ID') + ':' + config.get('KEYCLOAK_SERVICE_ACCOUNT_SECRET'), 'utf-8')).decode('utf-8') data = 'grant_type=client_credentials' token_response = OAuthService.post(token_url, basic_auth_encoded, AuthHeaderType.BASIC, ContentType.FORM_URL_ENCODED, data) token = token_response.json().get('access_token') get_users_url = issuer_url.replace( '/auth/', '/auth/admin/') + '/users?max=999999999' get_users_response = OAuthService.get(get_users_url, token, AuthHeaderType.BEARER, ContentType.JSON) for user in get_users_response.json(): if user.get('firstName', None) or user.get('lastName', None): user_name = user['username'].lower() display_name = user.get('firstName', '') + ' ' + user.get( 'lastName', '') op.execute( f"update payment set created_name = '{display_name}' where lower(created_by) = '{user_name}'" ) op.execute( f"update payment set updated_name = '{display_name}' where lower(updated_by) = '{user_name}'" ) op.execute( f"update invoice set created_name = '{display_name}' where lower(created_by) = '{user_name}'" ) op.execute( f"update invoice set updated_name = '{display_name}' where lower(updated_by) = '{user_name}'" )
def check_auth(business_identifier: str, account_id: str = None, corp_type_code: str = None, **kwargs): # pylint: disable=unused-argument, too-many-branches, too-many-statements """Authorize the user for the business entity and return authorization response.""" user: UserContext = kwargs['user'] is_authorized: bool = False auth_response = None product_code = CodeService.find_code_value_by_type_and_code( Code.CORP_TYPE.value, corp_type_code).get('product') account_id = account_id or user.account_id call_auth_svc: bool = True if user.is_system(): is_authorized = bool(Role.EDITOR.value in user.roles) if product_code: is_authorized = is_authorized and product_code == user.product_code # Call auth only if it's business (entities), as affiliation is the auhtorization if user.product_code != 'BUSINESS': call_auth_svc = False # Add account name as the service client name auth_response = { 'account': { 'id': user.account_id or user.user_name } } if call_auth_svc: bearer_token = user.bearer_token current_app.logger.info( f'Checking auth for Account : {account_id}, Business : {business_identifier}, ' f'Is Staff : {user.is_staff()}') roles: list = [] auth_response = {} if account_id: auth_url = current_app.config.get('AUTH_API_ENDPOINT') + f'orgs/{account_id}' \ f'/authorizations?expanded=true' additional_headers = None if corp_type_code: additional_headers = {'Product-Code': product_code} auth_response = RestService.get( auth_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON, additional_headers=additional_headers).json() roles: list = auth_response.get('roles', []) g.account_id = account_id elif business_identifier: auth_url = current_app.config.get( 'AUTH_API_ENDPOINT' ) + f'entities/{business_identifier}/authorizations?expanded=true' auth_response = RestService.get(auth_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON).json() roles: list = auth_response.get('roles', []) g.account_id = auth_response.get('account').get( 'id') if auth_response.get('account', None) else None elif user.is_staff(): roles: list = user.roles auth_response = {} else: current_app.logger.info('No Auth Information Found') g.user_permission = auth_response.get('roles') if kwargs.get('one_of_roles', None): is_authorized = len( list(set(kwargs.get('one_of_roles')) & set(roles))) > 0 if kwargs.get('contains_role', None): is_authorized = kwargs.get('contains_role') in roles # Check if premium flag is required if kwargs.get('is_premium', False) and auth_response['account'][ 'accountType'] != AccountType.PREMIUM.value: is_authorized = False # For staff users, if the account is coming as empty add stub data # (businesses which are not affiliated won't have account) if user.is_staff() and not auth_response.get('account', None): auth_response['account'] = { 'id': f'PASSCODE_ACCOUNT_{business_identifier}' } if user.is_system() and bool(Role.EDITOR.value in user.roles): is_authorized = True if not is_authorized: abort(403) # IF auth response is empty (means a service account or a business with no account by staff) if not auth_response: if Role.SYSTEM.value in user.roles: # Call auth only if it's business (entities) # Add account name as the service client name auth_response = { 'account': { 'id': user.user_name, 'paymentInfo': { 'methodOfPayment': PaymentMethod.DIRECT_PAY.value } } } return auth_response
def check_auth(business_identifier: str, account_id: str = None, corp_type_code: str = None, **kwargs): # pylint: disable=unused-argument """Authorize the user for the business entity and return authorization response.""" user: UserContext = kwargs['user'] is_authorized: bool = False auth_response = None is_business_auth = True is_service_account = False if not account_id: account_id = user.account_id if Role.SYSTEM.value in user.roles: is_authorized = bool(Role.EDITOR.value in user.roles) is_service_account = True else: bearer_token = user.bearer_token if business_identifier: auth_url = current_app.config.get( 'AUTH_API_ENDPOINT' ) + f'entities/{business_identifier}/authorizations?expanded=true' auth_response = RestService.get(auth_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON).json() roles: list = auth_response.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 elif account_id: is_business_auth = False # TODO For now, just make a call to /orgs/<id> to see if the user has access to the account # When product level subscription is in place, use the below commented code. # auth_url = current_app.config.get( # 'AUTH_API_ENDPOINT') + f'accounts/{account_id}/products/{corp_type_code}/authorizations?expanded=true' # auth_response = RestService.get(auth_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON).json() # roles: list = auth_response.get('roles', []) # if roles: # is_authorized = True # TODO Remove the below call and uncomment the above code once product subscription is in place. account_url = current_app.config.get( 'AUTH_API_ENDPOINT') + f'orgs/{account_id}' account_response = RestService.get(account_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON, retry_on_failure=True).json() auth_response = {} is_authorized = True if not is_authorized: abort(403) # TODO Remove this code once the authorizations endpoint returns the account details if not is_service_account: if is_business_auth: account_url = current_app.config.get( 'AUTH_API_ENDPOINT' ) + f'orgs?affiliation={business_identifier}' account_response = RestService.get( account_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON, retry_on_failure=True).json().get('orgs')[0] # else: # account_url = current_app.config.get( # 'AUTH_API_ENDPOINT') + f'orgs/{account_id}' # account_response = RestService.get(account_url, bearer_token, AuthHeaderType.BEARER, ContentType.JSON, # retry_on_failure=True).json() auth_response['account'] = {'paymentPreference': {}} auth_response['account']['id'] = account_response['id'] auth_response['account']['name'] = account_response['name'] auth_response['account']['paymentPreference'][ 'methodOfPayment'] = account_response['preferred_payment'] auth_response['account']['paymentPreference'][ 'bcOnlineUserId'] = account_response.get('bc_online_user_id', None) return auth_response