def create_master_organisation(test_client, init_database, external_reserve_token): from server.models.organisation import Organisation master_organisation = Organisation.master_organisation() if master_organisation is None: print('Creating master organisation') master_organisation = Organisation(name='FrancineCorp', is_master=True, token=external_reserve_token, country_code='AU') db.session.add(master_organisation) db.session.commit() return master_organisation
def approve_and_disburse(self, initial_disbursement=None): from server.utils.access_control import AccessControl admin = getattr(g, 'user', None) active_org = getattr(g, 'active_organisation', Organisation.master_organisation()) if initial_disbursement is None: # initial disbursement defaults to None. If initial_disbursement is set then skip this section. # If none, then we want to see if the active_org has a default disbursement amount initial_disbursement = active_org.default_disbursement # Baseline is NOT is_approved, and do NOT auto_resolve self.is_approved = False auto_resolve = False # If admin role is admin or higher, then auto-approval is contingent on being less than or # equal to the default disbursement if (admin and AccessControl.has_sufficient_tier(admin.roles, 'ADMIN', 'admin'))or ( g.get('auth_type') == 'external' and active_org.auto_approve_externally_created_users ): self.is_approved = True if initial_disbursement <= active_org.default_disbursement: auto_resolve = True # Accounts created by superadmins are all approved, and their disbursements are # auto-resolved no matter how big they are! if admin and AccessControl.has_sufficient_tier(admin.roles, 'ADMIN', 'superadmin'): self.is_approved = True auto_resolve = True if self.is_beneficiary: # Initial disbursement should be pending if the account is not approved disbursement = self._make_initial_disbursement(initial_disbursement, auto_resolve=auto_resolve) return disbursement
def create_organisation(test_client, init_database, external_reserve_token, create_master_organisation): from server.models.organisation import Organisation organisation = Organisation(name='Sempo', token=external_reserve_token, default_disbursement=400, country_code='US') db.session.add(organisation) db.session.commit() return organisation
def proccess_phone_number(phone_number, region=None, ignore_region=False): """ Parse any given phone number. :param phone_number: int :param region: ISO 3166-1 alpha-2 codes :param ignore_region: Boolean. True returns original phone :return: """ from server.models.organisation import Organisation if phone_number is None: return None if ignore_region: return phone_number if region is None: try: region = g.active_organisation.country_code except AttributeError: region = Organisation.master_organisation().country_code if not isinstance(phone_number, str): try: phone_number = str(int(phone_number)) except ValueError: pass phone_number_object = phonenumbers.parse(phone_number, region) parsed_phone_number = phonenumbers.format_number(phone_number_object, phonenumbers.PhoneNumberFormat.E164) return parsed_phone_number
def _make_initial_disbursement(self, initial_disbursement, auto_resolve=False): from server.utils.credit_transfer import make_payment_transfer active_org = getattr(g, 'active_organisation', Organisation.master_organisation()) initial_disbursement = initial_disbursement or active_org.default_disbursement if not initial_disbursement: return None user_id = get_authorising_user_id() if user_id is not None: sender = User.query.execution_options(show_all=True).get(user_id) else: sender = self.primary_user disbursement = make_payment_transfer( initial_disbursement, token=self.token, send_user=sender, receive_user=self.primary_user, transfer_subtype=TransferSubTypeEnum.DISBURSEMENT, is_ghost_transfer=False, require_sender_approved=False, require_recipient_approved=False, automatically_resolve_complete=auto_resolve) return disbursement
def create_master_organisation(reserve_token): print_section_title('Creating/Updating Master Organisation') master_organisation = Organisation.master_organisation() if master_organisation is None: print('Creating master organisation') if reserve_token: print('Binding to reserve token') master_organisation = Organisation(name='Reserve', is_master=True, token=reserve_token) db.session.add(master_organisation) db.session.commit() print_section_conclusion('Done creating master organisation')
def create_master_organisation(app, reserve_token): print_section_title('Creating/Updating Master Organisation') master_organisation = Organisation.master_organisation() if master_organisation is None: print('Creating master organisation') if reserve_token: print('Binding to reserve token') master_organisation = Organisation( name='Reserve', is_master=True, token=reserve_token, country_code=app.config.get('DEFAULT_COUNTRY', 'AU') ) db.session.add(master_organisation) db.session.commit() print_section_conclusion('Done creating master organisation')
def test_credit_transfer_organisation_filters(test_client, init_database, authed_sempo_admin_user, complete_admin_auth_token, create_credit_transfer, query_organisations, status_code): # Checks that the credit_transfer endpoint supports multiple organisations url = f'/api/v1/credit_transfer/?query_organisations={query_organisations}' from server.models.organisation import Organisation # master_organisation is organisation 1 master_organisation = Organisation.master_organisation() create_credit_transfer.sender_user.organisation = master_organisation create_credit_transfer.recipient_user.organisation = master_organisation create_credit_transfer.sender_transfer_account.organisation = master_organisation create_credit_transfer.recipient_transfer_account.organisation = master_organisation init_database.session.commit() # Add master_organisation (organisation 1) to our user's organisations. admin_user already # is a member of organisation 2 authed_sempo_admin_user.organisations.append(master_organisation) response = test_client.get(url, headers=dict( Authorization=complete_admin_auth_token, Accept='application/json')) all_transfers = CreditTransfer.query.execution_options(show_all=True).all() org1 = Organisation.query.get(1) org2 = Organisation.query.get(2) org1_transfer_ids = set(t.id for t in all_transfers if org1 in t.organisations) org2_transfer_ids = set(t.id for t in all_transfers if org2 in t.organisations) assert response.status_code == status_code if status_code == 200: response_ids = set() for r in response.json['data'].get('credit_transfers', []): response_ids.add(r['id']) if '1' in query_organisations: assert create_credit_transfer.id in response_ids if query_organisations == '1': assert response_ids == org1_transfer_ids if query_organisations == '2' or query_organisations == '': assert response_ids == org2_transfer_ids if query_organisations == '1,2': assert response_ids == org1_transfer_ids.union(org2_transfer_ids)
def default_token(user: User) -> Token: try: transfer_account = default_transfer_account(user) token = transfer_account.token except TransferAccountNotFoundError: if user.default_organisation is not None: token = user.default_organisation.token else: token = Organisation.master_organisation().token if token is None: raise Exception('no default token for user') return token
def add_user_to_organisation(self, organisation: Organisation, is_admin=False): if not self.default_organisation: self.default_organisation = organisation self.organisations.append(organisation) if is_admin and organisation.org_level_transfer_account_id: if organisation.org_level_transfer_account is None: organisation.org_level_transfer_account = ( db.session.query(server.models.transfer_account.TransferAccount) .execution_options(show_all=True) .get(organisation.org_level_transfer_account_id)) self.transfer_accounts.append(organisation.org_level_transfer_account)
def __init__(self, blockchain_address: Optional[str] = None, bound_entity: Optional[Union[Organisation, User]] = None, account_type: Optional[TransferAccountType] = None, private_key: Optional[str] = None, **kwargs): super(TransferAccount, self).__init__(**kwargs) if bound_entity: bound_entity.transfer_accounts.append(self) if isinstance(bound_entity, Organisation): self.account_type = TransferAccountType.ORGANISATION self.blockchain_address = bound_entity.primary_blockchain_address self._bind_to_organisation(bound_entity) elif isinstance(bound_entity, User): self.account_type = TransferAccountType.USER self.blockchain_address = bound_entity.primary_blockchain_address if bound_entity.default_organisation: self._bind_to_organisation( bound_entity.default_organisation) elif isinstance(bound_entity, ExchangeContract): self.account_type = TransferAccountType.CONTRACT self.blockchain_address = bound_entity.blockchain_address self.is_public = True self.exchange_contact = self if not self.organisation: master_organisation = Organisation.master_organisation() if not master_organisation: print('master_organisation not found') if master_organisation: self._bind_to_organisation(master_organisation) if blockchain_address: self.blockchain_address = blockchain_address if not self.blockchain_address: self.blockchain_address = bt.create_blockchain_wallet( private_key=private_key) if account_type: self.account_type = account_type
def test_get_user(test_client, authed_sempo_admin_user, create_transfer_account_user): # Checks that the get_user endpoint supports multiple organisations from server.models.organisation import Organisation master_organisation = Organisation.master_organisation() authed_sempo_admin_user.organisations.append(master_organisation) create_transfer_account_user.organisations = [master_organisation] db.session.commit() authed_sempo_admin_user.set_held_role('ADMIN', 'superadmin') auth = get_complete_auth_token(authed_sempo_admin_user) def get_user_endpoint(query_organisations): return test_client.get( f"/api/v1/user/?query_organisations={query_organisations}", headers=dict( Authorization=auth, Accept='application/json' )) def get_transfer_account_ids(users): transfer_account_ids = [] for user in users: transfer_account_ids.append(user['id']) return transfer_account_ids # User 1 is in both orgs # User 3 is in Org 2 # User 4 is in Org 2 # User 5 is in Org 2 response = get_user_endpoint('1,2') assert response.status_code == 200 users_list = response.json['data']['users'] assert get_transfer_account_ids(users_list) == [5, 4, 3, 1] response = get_user_endpoint('1') assert response.status_code == 200 users_list = response.json['data']['users'] assert get_transfer_account_ids(users_list) == [1] response = get_user_endpoint('2') assert response.status_code == 200 users_list = response.json['data']['users'] assert get_transfer_account_ids(users_list) == [5, 4, 3, 1]
def approve_initial_disbursement(self): from server.utils.access_control import AccessControl admin = getattr(g, 'user', None) active_org = getattr(g, 'active_organisation', Organisation.master_organisation()) initial_disbursement = db.session.query(server.models.credit_transfer.CreditTransfer)\ .filter(server.models.credit_transfer.CreditTransfer.recipient_user == self.primary_user)\ .filter(server.models.credit_transfer.CreditTransfer.is_initial_disbursement == True)\ .first() if initial_disbursement and initial_disbursement.transfer_status == TransferStatusEnum.PENDING: # Must be superadmin to auto-resolve something over default disbursement if initial_disbursement.transfer_amount > active_org.default_disbursement: if admin and AccessControl.has_sufficient_tier(admin.roles, 'ADMIN', 'superadmin'): return initial_disbursement.resolve_as_complete_and_trigger_blockchain(queue='high-priority') else: return False else: return initial_disbursement.resolve_as_complete_and_trigger_blockchain(queue='high-priority')
def post(self): # get the post data post_data = request.get_json() email = post_data.get('email') or post_data.get('username') password = post_data.get('password') phone = post_data.get('phone') referral_code = post_data.get('referral_code') if phone is not None: # this is a registration from a mobile device THUS a vendor or recipient. response_object, response_code = UserUtils.proccess_create_or_modify_user_request( post_data, is_self_sign_up=True, ) if response_code == 200: db.session.commit() return make_response(jsonify(response_object)), response_code email_ok = False whitelisted_emails = EmailWhitelist.query\ .filter_by(referral_code=referral_code, used=False) \ .execution_options(show_all=True).all() selected_whitelist_item = None exact_match = False tier = None sempoadmin_emails = current_app.config['SEMPOADMIN_EMAILS'] if sempoadmin_emails != [''] and email in sempoadmin_emails: email_ok = True tier = 'sempoadmin' for whitelisted in whitelisted_emails: if whitelisted.allow_partial_match and whitelisted.email in email: email_ok = True tier = whitelisted.tier selected_whitelist_item = whitelisted exact_match = False continue elif whitelisted.email == email: email_ok = True whitelisted.used = True tier = whitelisted.tier selected_whitelist_item = whitelisted exact_match = True continue if not email_ok: response_object = { 'status': 'fail', 'message': 'Invalid email domain.', } return make_response(jsonify(response_object)), 403 if len(password) < 7: response_object = { 'status': 'fail', 'message': 'Password must be at least 6 characters long', } return make_response(jsonify(response_object)), 403 # check if user already exists user = User.query.filter_by(email=email).execution_options(show_all=True).first() if user: response_object = { 'status': 'fail', 'message': 'User already exists. Please Log in.', } return make_response(jsonify(response_object)), 403 if tier is None: tier = 'subadmin' if selected_whitelist_item: organisation = selected_whitelist_item.organisation else: organisation = Organisation.master_organisation() user = User(blockchain_address=organisation.primary_blockchain_address) user.create_admin_auth(email, password, tier, organisation) # insert the user db.session.add(user) db.session.flush() if exact_match: user.is_activated = True auth_token = user.encode_auth_token() # Possible Outcomes: # TFA required, but not set up # TFA not required tfa_response_oject = tfa_logic(user, tfa_token=None) if tfa_response_oject: tfa_response_oject['auth_token'] = auth_token.decode() db.session.commit() # need this here to commit a created user to the db return make_response(jsonify(tfa_response_oject)), 401 # Update the last_seen TS for this user user.update_last_seen_ts() response_object = create_user_response_object(user, auth_token, 'Successfully activated.') db.session.commit() return make_response(jsonify(response_object)), 201 activation_token = user.encode_single_use_JWS('A') send_activation_email(activation_token, email) db.session.commit() # generate the auth token response_object = { 'status': 'success', 'message': 'Successfully registered. You must activate your email.', } return make_response(jsonify(response_object)), 201
def get_reserve_token(self): # reserve token is master token for now return Organisation.master_organisation().token
def create_transfer_account_user(first_name=None, last_name=None, preferred_language=None, phone=None, email=None, public_serial_number=None, organisation: Organisation = None, token=None, blockchain_address=None, transfer_account_name=None, lat=None, lng=None, use_precreated_pin=False, use_last_4_digits_of_id_as_initial_pin=False, existing_transfer_account=None, is_beneficiary=False, is_vendor=False, is_tokenagent=False, is_groupaccount=False, is_self_sign_up=False, business_usage=None, initial_disbursement=None): user = User(first_name=first_name, last_name=last_name, lat=lat, lng=lng, preferred_language=preferred_language, phone=phone, email=email, public_serial_number=public_serial_number, is_self_sign_up=is_self_sign_up, business_usage=business_usage) precreated_pin = None is_activated = False try: transfer_card = TransferCard.get_transfer_card(public_serial_number) except Exception as e: transfer_card = None if use_precreated_pin: precreated_pin = transfer_card.PIN is_activated = True elif use_last_4_digits_of_id_as_initial_pin: precreated_pin = str(public_serial_number or phone)[-4:] is_activated = False user.set_pin(precreated_pin, is_activated) if not is_vendor: vendor_tier = None elif existing_transfer_account: vendor_tier = 'vendor' else: vendor_tier = 'supervendor' user.set_held_role('VENDOR', vendor_tier) if is_tokenagent: user.set_held_role('TOKEN_AGENT', 'grassroots_token_agent') if is_groupaccount: user.set_held_role('GROUP_ACCOUNT', 'grassroots_group_account') if is_beneficiary: user.set_held_role('BENEFICIARY', 'beneficiary') if not organisation: organisation = Organisation.master_organisation() user.add_user_to_organisation(organisation, is_admin=False) db.session.add(user) if existing_transfer_account: transfer_account = existing_transfer_account user.transfer_accounts.append(existing_transfer_account) else: transfer_account = TransferAccount( bound_entity=user, blockchain_address=blockchain_address, organisation=organisation) transfer_account.name = transfer_account_name transfer_account.is_vendor = is_vendor transfer_account.is_beneficiary = is_beneficiary if transfer_card: transfer_account.transfer_card = transfer_card if token: transfer_account.token = token if not is_self_sign_up: transfer_account.approve_and_disburse( initial_disbursement=initial_disbursement) db.session.add(transfer_account) user.default_transfer_account = transfer_account return user
def post(self, organisation_id): post_data = request.get_json() organisation_name = post_data.get('organisation_name') custom_welcome_message_key = post_data.get( 'custom_welcome_message_key') timezone = post_data.get('timezone') country_code = post_data.get('country_code') default_disbursement = post_data.get('default_disbursement') minimum_vendor_payout_withdrawal = post_data.get( 'minimum_vendor_payout_withdrawal') require_transfer_card = post_data.get('require_transfer_card') default_lat = post_data.get('default_lat') default_lng = post_data.get('default_lng') account_types = post_data.get('account_types', []) token_id = post_data.get('token_id') deploy_cic = post_data.get('deploy_cic', False) for at in account_types: if at not in ASSIGNABLE_TIERS.keys(): raise Exception(f'{at} not an assignable role') if organisation_name is None or country_code is None or timezone is None: return make_response( jsonify({ 'message': 'Must provide organisation_name, country_code and timezone to create organisation.' })), 400 existing_organisation = Organisation.query.filter_by( name=organisation_name).execution_options(show_all=True).first() if existing_organisation is not None: return make_response( jsonify({ 'message': 'Must be unique name. Organisation already exists for name: {}' .format(organisation_name), 'data': { 'organisation': organisation_schema.dump(existing_organisation).data } })), 400 try: new_organisation = Organisation( name=organisation_name, custom_welcome_message_key=custom_welcome_message_key, timezone=timezone, country_code=country_code, default_disbursement=default_disbursement, minimum_vendor_payout_withdrawal= minimum_vendor_payout_withdrawal, require_transfer_card=require_transfer_card, default_lat=default_lat, default_lng=default_lng, valid_roles=account_types) except Exception as e: response_object = { 'message': str(e), } return make_response(jsonify(response_object)), 400 db.session.add(new_organisation) db.session.flush() response_object = { 'message': 'Created Project', 'data': { 'organisation': organisation_schema.dump(new_organisation).data }, } if token_id: token = Token.query.get(token_id) if token is None: return make_response(jsonify({'message': 'Token not found'})), 404 new_organisation.bind_token(token) elif deploy_cic: cic_response_object, cic_response_code = deploy_cic_token( post_data, new_organisation) if cic_response_code == 201: response_object['data']['token_id'] = cic_response_object[ 'data']['token_id'] else: return make_response( jsonify(cic_response_object)), cic_response_code if AccessControl.has_suffient_role(g.user.roles, {'ADMIN': 'superadmin'}): g.user.add_user_to_organisation(new_organisation, is_admin=True) return make_response(jsonify(response_object)), 201
def post(self, organisation_id): post_data = request.get_json() organisation_name = post_data.get('organisation_name') custom_welcome_message_key = post_data.get( 'custom_welcome_message_key') timezone = post_data.get('timezone') country_code = post_data.get('country_code') default_disbursement = post_data.get('default_disbursement') require_transfer_card = post_data.get('require_transfer_card') default_lat = post_data.get('default_lat') default_lng = post_data.get('default_lng') token_id = post_data.get('token_id') deploy_cic = post_data.get('deploy_cic', False) if organisation_name is None or country_code is None: return make_response( jsonify({ 'message': 'Must provide name and ISO 2 country_code to create organisation.' })), 400 existing_organisation = Organisation.query.filter_by( name=organisation_name).execution_options(show_all=True).first() if existing_organisation is not None: return make_response( jsonify({ 'message': 'Must be unique name. Organisation already exists for name: {}' .format(organisation_name), 'data': { 'organisation': organisation_schema.dump(existing_organisation).data } })), 400 try: new_organisation = Organisation( name=organisation_name, custom_welcome_message_key=custom_welcome_message_key, timezone=timezone, country_code=country_code, default_disbursement=default_disbursement, require_transfer_card=require_transfer_card, default_lat=default_lat, default_lng=default_lng) except Exception as e: response_object = { 'message': str(e), } return make_response(jsonify(response_object)), 400 db.session.add(new_organisation) db.session.flush() response_object = { 'message': 'Created Organisation', 'data': { 'organisation': organisation_schema.dump(new_organisation).data }, } if token_id: token = Token.query.get(token_id) if token is None: return make_response(jsonify({'message': 'Token not found'})), 404 new_organisation.bind_token(token) elif deploy_cic: cic_response_object, cic_response_code = deploy_cic_token( post_data, new_organisation) if cic_response_code == 201: response_object['data']['token_id'] = cic_response_object[ 'data']['token_id'] else: return make_response( jsonify(cic_response_object)), cic_response_code return make_response(jsonify(response_object)), 201
def run_setup(): app = create_app() ctx = app.app_context() ctx.push() # To simplify creation, we set the flask context to show all model data g.show_all = True master_organisation = Organisation.master_organisation() if master_organisation is None: print('Creating master organisation') master_organisation = Organisation(is_master=True) db.session.add(master_organisation) master_system_address = master_organisation.system_blockchain_address load_account(master_system_address, int(1e18)) reserve_token = get_or_create_reserve_token(master_system_address, 'AUD Token', 'AUD') master_organisation.token = reserve_token print('Creating organisation') new_organisation = get_or_create_organisation('org1', reserve_token) load_account(new_organisation.system_blockchain_address, int(1e18)) print('Creating admin user') amount_to_load = 1000 admin_user = get_or_create_admin_user('*****@*****.**', 'TestPassword', new_organisation) admin_transfer_account = admin_user.transfer_account load_account(admin_transfer_account.blockchain_address, int(20e18)) send_eth_task = bt.send_eth( signing_address=admin_transfer_account.blockchain_address, recipient_address=reserve_token.address, amount_wei=amount_to_load * int(1e16)) bt.await_task_success(send_eth_task) admin_user.transfer_account.balance = amount_to_load print('Creating Transfer Usage') usages = list(map( get_or_create_transfer_usage, ['Broken Pencils', 'Off Milk', 'Stuxnet', 'Used Playing Cards', '09 F9', 'Junk Mail', 'Cutlery', 'Leaked Private Keys', 'Parking Infringements', 'Betamax Movies', 'Hyperallergenic Soap', 'Dioxygen Difluoride', 'Hunter2' ])) print('Create a list of users with a different business usage id ') user_list = create_users_different_transer_usage(15, new_organisation) print('Making Bulk Transfers') seed_transfers(user_list, admin_user, reserve_token) print('Deploying Smart Token') smart_token, exchange_contract, registry_address = create_or_get_reserve_token( deploying_address=admin_transfer_account.blockchain_address, reserve_token=reserve_token, reserve_deposit_wei=5e17, issue_amount_wei=5e17, name='CIC1', symbol='CIC1', reserve_ratio_ppm=250000 ) print('Making exchanges') make_exchange( user=admin_user, from_token=reserve_token, to_token=smart_token, from_amount=2 ) create_transfer( amount=10, sender_user=admin_user, recipient_user=user_list[0], token=reserve_token ) make_exchange( user=user_list[0], from_token=reserve_token, to_token=smart_token, from_amount=10 ) create_transfer( amount=2, sender_user=user_list[0], recipient_user=user_list[1], token=smart_token ) db.session.commit() ctx.pop()
def deploy_cic_token(post_data, creating_org=None): name = post_data['name'] symbol = post_data['symbol'] decimals = post_data.get('decimals', 18) issue_amount_wei = int(post_data['issue_amount_wei']) reserve_deposit_wei = int(post_data['reserve_deposit_wei']) exchange_contract_id = post_data['exchange_contract_id'] reserve_ratio_ppm = post_data.get('reserve_ratio_ppm', 250000) if creating_org: deploying_address = creating_org.primary_blockchain_address else: deploying_address = g.user.primary_blockchain_address if not exchange_contract_id: response_object = { 'message': 'Must supply exchange contract id if deploying smart token contract' } return response_object, 400 exchange_contract = ExchangeContract.query.get(exchange_contract_id) if not exchange_contract: response_object = { 'message': 'Exchange contract not found for id {}'.format(exchange_contract_id) } return response_object, 400 balance_wei = bt.get_wallet_balance(deploying_address, exchange_contract.reserve_token) if balance_wei < reserve_deposit_wei: load_amount = int((reserve_deposit_wei - balance_wei) / 1e16) master_org = Organisation.master_organisation() print(f'Insufficient reserve funds (balance in wei: {balance_wei}), loading') if master_org.org_level_transfer_account.balance < load_amount: response_object = { 'message': f'Insufficient reserve funds for both deploying account ({balance_wei} wei), ' f'and master ({master_org.org_level_transfer_account.balance * 1e16} wei)' } return response_object, 400 load_task_uuid = bt.make_token_transfer( signing_address=master_org.primary_blockchain_address, token=exchange_contract.reserve_token, from_address=master_org.primary_blockchain_address, to_address=deploying_address, amount=load_amount ) try: bt.await_task_success(load_task_uuid) except TimeoutError: response_object = { 'message': f'Insufficient reserve funds (balance in wei: {balance_wei}), and could not load from master' } return response_object, 400 master_org.org_level_transfer_account.balance -= load_amount token = Token(name=name, symbol=symbol, token_type=TokenType.LIQUID) db.session.add(token) db.session.flush() deploy_data = dict( deploying_address=deploying_address, name=name, symbol=symbol, decimals=decimals, reserve_deposit_wei=reserve_deposit_wei, issue_amount_wei=issue_amount_wei, contract_registry_address=exchange_contract.contract_registry_blockchain_address, reserve_token_address=exchange_contract.reserve_token.address, reserve_ratio_ppm=reserve_ratio_ppm ) @copy_current_request_context def deploy(_deploy_data, _token_id, _exchange_contract_id, _creating_org_id=None): smart_token_result = bt.deploy_smart_token(**_deploy_data) address = smart_token_result['smart_token_address'] subexchange_address = smart_token_result['subexchange_address'] _token = Token.query.get(_token_id) _token.address = address _exchange_contract = ExchangeContract.query.get(_exchange_contract_id) _exchange_contract.add_token(_token, subexchange_address, reserve_ratio_ppm) if _creating_org_id: _creating_org = Organisation.query.get(_creating_org_id) _creating_org.bind_token(_token) _creating_org.org_level_transfer_account.balance = int(_deploy_data['issue_amount_wei'] / 1e16) bal = bt.get_wallet_balance(_creating_org.primary_blockchain_address, _token) print(f'Balance is {bal}') db.session.commit() if creating_org: creating_org_id = creating_org.id else: creating_org_id = None t = threading.Thread(target=deploy, args=(deploy_data, token.id, exchange_contract_id, creating_org_id)) t.daemon = True t.start() response_object = { 'message': 'success', 'data': { 'token_id': token.id } } return response_object, 201
def post(self): # There is an unique case where users are using their mobile number from the App to either login or register # The app uses g.active_organisation to reference user.transfer_account to send an SMS to the User. # This means that g.active_organisation should default to the master_organisation # For admin users, it doesn't matter as this endpoint is unauthed. g.active_organisation = Organisation.master_organisation() post_data = request.get_json() user = None phone = None email = post_data.get('username', '') or post_data.get('email', '') email = email.lower() if email else '' password = post_data.get('password') # Default pin to password as fallback for old android versions pin = post_data.get('pin', password) tfa_token = post_data.get('tfa_token') password_empty = password == '' or password is None pin_empty = pin == '' or pin is None ratelimit_key = email or post_data.get('phone') if ratelimit_key: limit = rate_limit("login_"+ratelimit_key, 25) if limit: response_object = { 'status': 'fail', 'message': f'Please try again in {limit} minutes' } return make_response(jsonify(response_object)), 403 # First try to match email if email: user = User.query.filter(func.lower(User.email)==email).execution_options(show_all=True).first() # Now try to match the public serial number (comes in under the phone) if not user: public_serial_number_or_phone = post_data.get('phone') user = User.query.filter_by(public_serial_number=public_serial_number_or_phone).execution_options( show_all=True).first() # Now try to match the phone if not user: try: phone = proccess_phone_number(post_data.get('phone'), region=post_data.get('region')) except NumberParseException as e: response_object = {'message': 'Invalid Phone Number: ' + str(e)} return make_response(jsonify(response_object)), 401 if phone: user = User.query.filter_by(phone=phone).execution_options(show_all=True).first() # mobile user doesn't exist so default to creating a new wallet! if user is None and phone and current_app.config['ALLOW_SELF_SIGN_UP']: # this is a registration from a mobile device THUS a vendor or recipient. response_object, response_code = UserUtils.proccess_create_or_modify_user_request( dict(phone=phone, deviceInfo=post_data.get('deviceInfo')), is_self_sign_up=True, ) if response_code == 200: db.session.commit() return make_response(jsonify(response_object)), response_code no_password_or_pin_hash = user and not user.password_hash and not user.pin_hash if post_data.get('phone') and user and user.one_time_code and (not user.is_activated or not user.pin_hash): # vendor sign up with one time code or OTP verified if user.one_time_code == pin: response_object = { 'status': 'success', 'pin_must_be_set': True, 'message': 'Please set your pin.' } return make_response(jsonify(attach_host(response_object))), 200 if not user.is_phone_verified or no_password_or_pin_hash: if user.is_self_sign_up: # self sign up, resend phone verification code user.set_pin(None, False) # resets PIN UserUtils.send_one_time_code(phone=phone, user=user) db.session.commit() if not password_empty: # The user provided a password, so probably not going through incremental login # This is a hacky way to get past the incremental-login multi-org split response_object = { 'status': 'fail', 'otp_verify': True, 'message': 'Please verify phone number.', 'error_message': 'Incorrect One Time Code.' } return make_response(jsonify(attach_host(response_object))), 200 response_object = {'message': 'Please verify phone number.', 'otp_verify': True} return make_response(jsonify(attach_host(response_object))), 200 if user and user.is_activated and post_data.get('phone') and (password_empty and pin_empty): # user already exists, is activated. no password or pin provided, thus request PIN screen. # todo: this should check if device exists, if no, resend OTP to verify login is real. response_object = { 'status': 'success', 'login_with_pin': True, 'message': 'Login with PIN' } return make_response(jsonify(attach_host(response_object))), 200 if not (email or post_data.get('phone')): response_object = { 'status': 'fail', 'message': 'No username supplied' } return make_response(jsonify(response_object)), 401 try: if not (user and (pin and user.verify_pin(pin) or password and user.verify_password(password))): response_object = { 'status': 'fail', 'message': 'Invalid username or password' } return make_response(jsonify(response_object)), 401 if not user.is_activated: response_object = { 'status': 'fail', 'is_activated': False, 'message': 'Account has not been activated. Please check your emails.' } return make_response(jsonify(response_object)), 401 if post_data.get('deviceInfo'): deviceInfo = post_data.get('deviceInfo') UserUtils.save_device_info(deviceInfo, user) auth_token = user.encode_auth_token() if not auth_token: response_object = { 'status': 'fail', 'message': 'Invalid username or password' } return make_response(jsonify(response_object)), 401 # Possible Outcomes: # TFA required, but not set up # TFA enabled, and user does not have valid TFA token # TFA enabled, and user has valid TFA token # TFA not required tfa_response_oject = tfa_logic(user, tfa_token) if tfa_response_oject: tfa_response_oject['auth_token'] = auth_token.decode() return make_response(jsonify(tfa_response_oject)), 401 # Update the last_seen TS for this user user.update_last_seen_ts() response_object = create_user_response_object(user, auth_token, 'Successfully logged in.') db.session.commit() return make_response(jsonify(attach_host(response_object))), 200 except Exception as e: sentry_sdk.capture_exception(e) raise e
def create_transfer_account_user(first_name=None, last_name=None, preferred_language=None, phone=None, email=None, public_serial_number=None, uuid=None, organisation: Organisation = None, token=None, blockchain_address=None, transfer_account_name=None, use_precreated_pin=False, use_last_4_digits_of_id_as_initial_pin=False, existing_transfer_account=None, roles=None, is_self_sign_up=False, business_usage=None, initial_disbursement=None): user = User(first_name=first_name, last_name=last_name, preferred_language=preferred_language, blockchain_address=blockchain_address, phone=phone, email=email, uuid=uuid, public_serial_number=public_serial_number, is_self_sign_up=is_self_sign_up, business_usage=business_usage) precreated_pin = None is_activated = False try: transfer_card = TransferCard.get_transfer_card(public_serial_number) except Exception as e: transfer_card = None if use_precreated_pin: precreated_pin = transfer_card.PIN is_activated = True elif use_last_4_digits_of_id_as_initial_pin: precreated_pin = str(public_serial_number or phone)[-4:] is_activated = False user.set_pin(precreated_pin, is_activated) if roles: for role in roles: user.set_held_role(role[0], role[1]) else: user.remove_all_held_roles() if not organisation: organisation = Organisation.master_organisation() user.add_user_to_organisation(organisation, is_admin=False) db.session.add(user) if existing_transfer_account: transfer_account = existing_transfer_account user.transfer_accounts.append(existing_transfer_account) else: transfer_account = TransferAccount( bound_entity=user, blockchain_address=blockchain_address, organisation=organisation) top_level_roles = [r[0] for r in roles or []] is_vendor = 'VENDOR' in top_level_roles is_beneficiary = 'BENEFICIARY' in top_level_roles transfer_account.name = transfer_account_name transfer_account.is_vendor = is_vendor transfer_account.is_beneficiary = is_beneficiary if transfer_card: transfer_account.transfer_card = transfer_card if token: transfer_account.token = token if not is_self_sign_up: transfer_account.approve_and_disburse( initial_disbursement=initial_disbursement) db.session.add(transfer_account) user.default_transfer_account = transfer_account return user