Merge branch 'philip/social-pin-recovery' into 'master'
Philip/social pin recovery See merge request grassrootseconomics/cic-internal-integration!311
This commit is contained in:
commit
cd102807a6
@ -24,6 +24,8 @@ def upgrade():
|
||||
sa.Column('preferred_language', sa.String(), nullable=True),
|
||||
sa.Column('password_hash', sa.String(), nullable=True),
|
||||
sa.Column('failed_pin_attempts', sa.Integer(), nullable=False),
|
||||
sa.Column('guardians', sa.String(), nullable=True),
|
||||
sa.Column('guardian_quora', sa.Integer(), nullable=False),
|
||||
sa.Column('status', sa.Integer(), nullable=False),
|
||||
sa.Column('created', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated', sa.DateTime(), nullable=False),
|
||||
|
@ -4,6 +4,8 @@ import json
|
||||
# external imports
|
||||
from cic_eth.api import Api
|
||||
from cic_types.condiments import MetadataPointer
|
||||
from sqlalchemy import Column, Integer, String
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.metadata import get_cached_preferred_language, parse_account_metadata
|
||||
@ -12,8 +14,9 @@ from cic_ussd.db.enum import AccountStatus
|
||||
from cic_ussd.db.models.base import SessionBase
|
||||
from cic_ussd.db.models.task_tracker import TaskTracker
|
||||
from cic_ussd.encoder import check_password_hash, create_password_hash
|
||||
from sqlalchemy import Column, Integer, String
|
||||
from sqlalchemy.orm.session import Session
|
||||
from cic_ussd.phone_number import Support
|
||||
|
||||
support_phone = Support.phone_number
|
||||
|
||||
|
||||
class Account(SessionBase):
|
||||
@ -29,12 +32,16 @@ class Account(SessionBase):
|
||||
failed_pin_attempts = Column(Integer)
|
||||
status = Column(Integer)
|
||||
preferred_language = Column(String)
|
||||
guardians = Column(String)
|
||||
guardian_quora = Column(Integer)
|
||||
|
||||
def __init__(self, blockchain_address, phone_number):
|
||||
self.blockchain_address = blockchain_address
|
||||
self.phone_number = phone_number
|
||||
self.password_hash = None
|
||||
self.failed_pin_attempts = 0
|
||||
# self.guardians = f'{support_phone}' if support_phone else None
|
||||
self.guardian_quora = 1
|
||||
self.status = AccountStatus.PENDING.value
|
||||
|
||||
def __repr__(self):
|
||||
@ -45,6 +52,28 @@ class Account(SessionBase):
|
||||
self.failed_pin_attempts = 0
|
||||
self.status = AccountStatus.ACTIVE.value
|
||||
|
||||
def add_guardian(self, phone_number: str):
|
||||
set_guardians = phone_number
|
||||
if self.guardians:
|
||||
set_guardians = self.guardians.split(',')
|
||||
set_guardians.append(phone_number)
|
||||
','.join(set_guardians)
|
||||
self.guardians = set_guardians
|
||||
|
||||
def remove_guardian(self, phone_number: str):
|
||||
set_guardians = self.guardians.split(',')
|
||||
set_guardians.remove(phone_number)
|
||||
if len(set_guardians) > 1:
|
||||
self.guardians = ','.join(set_guardians)
|
||||
else:
|
||||
self.guardians = set_guardians[0]
|
||||
|
||||
def get_guardians(self) -> list:
|
||||
return self.guardians.split(',') if self.guardians else []
|
||||
|
||||
def set_guardian_quora(self, quora: int):
|
||||
self.guardian_quora = quora
|
||||
|
||||
def create_password(self, password):
|
||||
"""This method takes a password value and hashes the value before assigning it to the corresponding
|
||||
`hashed_password` attribute in the user record.
|
||||
|
@ -317,6 +317,102 @@
|
||||
"display_key": "ussd.kenya.exit_successful_token_selection",
|
||||
"name": "exit_successful_token_selection",
|
||||
"parent": null
|
||||
},
|
||||
"54": {
|
||||
"description": "Pin management menu for operations related to an account's pin.",
|
||||
"display_key": "ussd.kenya.pin_management",
|
||||
"name": "pin_management",
|
||||
"parent": "start"
|
||||
},
|
||||
"55": {
|
||||
"description": "Phone number entry for account whose pin is being reset.",
|
||||
"display_key": "ussd.kenya.reset_guarded_pin",
|
||||
"name": "reset_guarded_pin",
|
||||
"parent": "pin_management"
|
||||
},
|
||||
"56": {
|
||||
"description": "Pin entry for initiating request to reset an account's pin.",
|
||||
"display_key": "ussd.kenya.reset_guarded_pin_authorization",
|
||||
"name": "reset_guarded_pin_authorization",
|
||||
"parent": "pin_management"
|
||||
},
|
||||
"57": {
|
||||
"description": "Exit menu following successful pin reset initiation.",
|
||||
"display_key": "ussd.kenya.exit_pin_reset_initiated_success",
|
||||
"name": "exit_pin_reset_initiated_success",
|
||||
"parent": "pin_management"
|
||||
},
|
||||
"58": {
|
||||
"description": "Exit menu in the event that an account is not a set guardian.",
|
||||
"display_key": "ussd.kenya.exit_not_authorized_for_pin_reset",
|
||||
"name": "exit_not_authorized_for_pin_reset",
|
||||
"parent": "pin_management"
|
||||
},
|
||||
"59": {
|
||||
"description": "Pin guard menu for handling guardianship operations.",
|
||||
"display_key": "ussd.kenya.guard_pin",
|
||||
"name": "guard_pin",
|
||||
"parent": "pin_management"
|
||||
},
|
||||
"60": {
|
||||
"description": "Pin entry to display a list of set guardians.",
|
||||
"display_key": "ussd.kenya.guardian_list_pin_authorization",
|
||||
"name": "guardian_list_pin_authorization",
|
||||
"parent": "guard_pin"
|
||||
},
|
||||
"61": {
|
||||
"description": "Menu to display list of set guardians.",
|
||||
"display_key": "ussd.kenya.guardian_list",
|
||||
"name": "guardian_list",
|
||||
"parent": "guard_pin"
|
||||
},
|
||||
"62": {
|
||||
"description": "Phone number entry to add an account as a guardian to reset pin.",
|
||||
"display_key": "ussd.kenya.add_guardian",
|
||||
"name": "add_guardian",
|
||||
"parent": "guard_pin"
|
||||
},
|
||||
"63": {
|
||||
"description": "Pin entry to confirm addition of an account as a guardian.",
|
||||
"display_key": "ussd.kenya.add_guardian_pin_authorization",
|
||||
"name": "add_guardian_pin_authorization",
|
||||
"parent": "guard_pin"
|
||||
},
|
||||
"64": {
|
||||
"description": "Exit menu when an account is successfully added as pin reset guardian.",
|
||||
"display_key": "ussd.kenya.exit_guardian_addition_success",
|
||||
"name": "exit_guardian_addition_success",
|
||||
"parent": "guard_pin"
|
||||
},
|
||||
"65": {
|
||||
"description": "Phone number entry to remove an account as a guardian to reset pin.",
|
||||
"display_key": "ussd.kenya.remove_guardian",
|
||||
"name": "remove_guardian",
|
||||
"parent": "guard_pin"
|
||||
},
|
||||
"66": {
|
||||
"description": "Pin entry to confirm removal of an account as a guardian.",
|
||||
"display_key": "ussd.kenya.remove_guardian_pin_authorization",
|
||||
"name": "remove_guardian_pin_authorization",
|
||||
"parent": "guard_pin"
|
||||
},
|
||||
"67": {
|
||||
"description": "Exit menu when an account is successfully removed as pin reset guardian.",
|
||||
"display_key": "ussd.kenya.exit_guardian_removal_success",
|
||||
"name": "exit_guardian_removal_success",
|
||||
"parent": "guard_pin"
|
||||
},
|
||||
"68": {
|
||||
"description": "Exit menu when invalid phone number entry for guardian addition. ",
|
||||
"display_key": "ussd.kenya.exit_invalid_guardian_addition",
|
||||
"name": "exit_invalid_guardian_addition",
|
||||
"parent": "guard_pin"
|
||||
},
|
||||
"69": {
|
||||
"description": "Exit menu when invalid phone number entry for guardian removal. ",
|
||||
"display_key": "ussd.kenya.exit_invalid_guardian_removal",
|
||||
"name": "exit_invalid_guardian_removal",
|
||||
"parent": "guard_pin"
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ from typing import Optional
|
||||
import phonenumbers
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import Account
|
||||
|
||||
|
||||
class E164Format:
|
||||
|
@ -121,6 +121,27 @@ class MenuProcessor:
|
||||
self.display_key, preferred_language, last_transaction_set=last_transaction_set
|
||||
)
|
||||
|
||||
def add_guardian_pin_authorization(self):
|
||||
guardian_information = self.guardian_metadata()
|
||||
return self.pin_authorization(guardian_information=guardian_information)
|
||||
|
||||
def guardian_list(self):
|
||||
preferred_language = get_cached_preferred_language(self.account.blockchain_address)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
set_guardians = self.account.get_guardians()
|
||||
if set_guardians:
|
||||
guardians_list = ''
|
||||
guardians_list_header = translation_for('helpers.guardians_list_header', preferred_language)
|
||||
for phone_number in set_guardians:
|
||||
guardian = Account.get_by_phone_number(phone_number, self.session)
|
||||
guardian_information = guardian.standard_metadata_id()
|
||||
guardians_list += f'{guardian_information}\n'
|
||||
guardians_list = guardians_list_header + '\n' + guardians_list
|
||||
else:
|
||||
guardians_list = translation_for('helpers.no_guardians_list', preferred_language)
|
||||
return translation_for(self.display_key, preferred_language, guardians_list=guardians_list)
|
||||
|
||||
def account_tokens(self) -> str:
|
||||
cached_token_data_list = get_cached_token_data_list(self.account.blockchain_address)
|
||||
token_data_list = parse_token_list(cached_token_data_list)
|
||||
@ -207,6 +228,20 @@ class MenuProcessor:
|
||||
f'{self.display_key}.retry', preferred_language, retry_pin_entry=retry_pin_entry
|
||||
)
|
||||
|
||||
def guarded_account_metadata(self):
|
||||
guarded_account_phone_number = self.ussd_session.get('data').get('guarded_account_phone_number')
|
||||
guarded_account = Account.get_by_phone_number(guarded_account_phone_number, self.session)
|
||||
return guarded_account.standard_metadata_id()
|
||||
|
||||
def guardian_metadata(self):
|
||||
guardian_phone_number = self.ussd_session.get('data').get('guardian_phone_number')
|
||||
guardian = Account.get_by_phone_number(guardian_phone_number, self.session)
|
||||
return guardian.standard_metadata_id()
|
||||
|
||||
def reset_guarded_pin_authorization(self):
|
||||
guarded_account_information = self.guarded_account_metadata()
|
||||
return self.pin_authorization(guarded_account_information=guarded_account_information)
|
||||
|
||||
def start_menu(self):
|
||||
"""
|
||||
:return:
|
||||
@ -277,6 +312,47 @@ class MenuProcessor:
|
||||
sender_information=tx_sender_information
|
||||
)
|
||||
|
||||
def exit_guardian_addition_success(self) -> str:
|
||||
guardian_information = self.guardian_metadata()
|
||||
preferred_language = get_cached_preferred_language(self.account.blockchain_address)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
return translation_for(self.display_key,
|
||||
preferred_language,
|
||||
guardian_information=guardian_information)
|
||||
|
||||
def exit_guardian_removal_success(self):
|
||||
guardian_information = self.guardian_metadata()
|
||||
preferred_language = get_cached_preferred_language(self.account.blockchain_address)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
return translation_for(self.display_key,
|
||||
preferred_language,
|
||||
guardian_information=guardian_information)
|
||||
|
||||
def exit_invalid_guardian_addition(self):
|
||||
failure_reason = self.ussd_session.get('data').get('failure_reason')
|
||||
preferred_language = get_cached_preferred_language(self.account.blockchain_address)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
return translation_for(self.display_key, preferred_language, error_exit=failure_reason)
|
||||
|
||||
def exit_invalid_guardian_removal(self):
|
||||
failure_reason = self.ussd_session.get('data').get('failure_reason')
|
||||
preferred_language = get_cached_preferred_language(self.account.blockchain_address)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
return translation_for(self.display_key, preferred_language, error_exit=failure_reason)
|
||||
|
||||
def exit_pin_reset_initiated_success(self):
|
||||
guarded_account_information = self.guarded_account_metadata()
|
||||
preferred_language = get_cached_preferred_language(self.account.blockchain_address)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
return translation_for(self.display_key,
|
||||
preferred_language,
|
||||
guarded_account_information=guarded_account_information)
|
||||
|
||||
def exit_insufficient_balance(self):
|
||||
"""
|
||||
:return:
|
||||
@ -379,18 +455,41 @@ def response(account: Account, display_key: str, menu_name: str, session: Sessio
|
||||
return menu_processor.transaction_pin_authorization()
|
||||
|
||||
if menu_name == 'token_selection_pin_authorization':
|
||||
logg.debug(f'RESPONSE IS: {menu_processor.token_selection_pin_authorization()}')
|
||||
return menu_processor.token_selection_pin_authorization()
|
||||
|
||||
if menu_name == 'exit_insufficient_balance':
|
||||
return menu_processor.exit_insufficient_balance()
|
||||
|
||||
if menu_name == 'exit_invalid_guardian_addition':
|
||||
return menu_processor.exit_invalid_guardian_addition()
|
||||
|
||||
if menu_name == 'exit_invalid_guardian_removal':
|
||||
return menu_processor.exit_invalid_guardian_removal()
|
||||
|
||||
if menu_name == 'exit_successful_transaction':
|
||||
return menu_processor.exit_successful_transaction()
|
||||
|
||||
if menu_name == 'exit_guardian_addition_success':
|
||||
return menu_processor.exit_guardian_addition_success()
|
||||
|
||||
if menu_name == 'exit_guardian_removal_success':
|
||||
return menu_processor.exit_guardian_removal_success()
|
||||
|
||||
if menu_name == 'exit_pin_reset_initiated_success':
|
||||
return menu_processor.exit_pin_reset_initiated_success()
|
||||
|
||||
if menu_name == 'account_balances':
|
||||
return menu_processor.account_balances()
|
||||
|
||||
if menu_name == 'guardian_list':
|
||||
return menu_processor.guardian_list()
|
||||
|
||||
if menu_name == 'add_guardian_pin_authorization':
|
||||
return menu_processor.add_guardian_pin_authorization()
|
||||
|
||||
if menu_name == 'reset_guarded_pin_authorization':
|
||||
return menu_processor.reset_guarded_pin_authorization()
|
||||
|
||||
if 'pin_authorization' in menu_name:
|
||||
return menu_processor.pin_authorization()
|
||||
|
||||
|
@ -81,6 +81,18 @@ def menu_six_selected(state_machine_data: Tuple[str, dict, Account, Session]) ->
|
||||
return user_input == '6'
|
||||
|
||||
|
||||
def menu_nine_selected(state_machine_data: Tuple[str, dict, Account, Session]) -> bool:
|
||||
"""
|
||||
This function checks that user input matches a string with value '6'
|
||||
:param state_machine_data: A tuple containing user input, a ussd session and user object.
|
||||
:type state_machine_data: tuple
|
||||
:return: A user input's match with '6'
|
||||
:rtype: bool
|
||||
"""
|
||||
user_input, ussd_session, account, session = state_machine_data
|
||||
return user_input == '9'
|
||||
|
||||
|
||||
def menu_zero_zero_selected(state_machine_data: Tuple[str, dict, Account, Session]) -> bool:
|
||||
"""
|
||||
This function checks that user input matches a string with value '00'
|
||||
|
200
apps/cic-ussd/cic_ussd/state_machine/logic/pin_guard.py
Normal file
200
apps/cic-ussd/cic_ussd/state_machine/logic/pin_guard.py
Normal file
@ -0,0 +1,200 @@
|
||||
# standard imports
|
||||
import logging
|
||||
from typing import Tuple
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
import i18n
|
||||
from phonenumbers.phonenumberutil import NumberParseException
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
# local imports
|
||||
from cic_ussd.account.metadata import get_cached_preferred_language
|
||||
from cic_ussd.db.models.account import Account
|
||||
from cic_ussd.db.models.base import SessionBase
|
||||
from cic_ussd.phone_number import process_phone_number, E164Format
|
||||
from cic_ussd.session.ussd_session import save_session_data
|
||||
from cic_ussd.translation import translation_for
|
||||
|
||||
|
||||
logg = logging.getLogger(__file__)
|
||||
|
||||
|
||||
def save_guardian_to_session_data(state_machine_data: Tuple[str, dict, Account, Session]):
|
||||
"""
|
||||
:param state_machine_data:
|
||||
:type state_machine_data:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
user_input, ussd_session, account, session = state_machine_data
|
||||
session_data = ussd_session.get('data') or {}
|
||||
guardian_phone_number = process_phone_number(phone_number=user_input, region=E164Format.region)
|
||||
session_data['guardian_phone_number'] = guardian_phone_number
|
||||
save_session_data('cic-ussd', session, session_data, ussd_session)
|
||||
|
||||
|
||||
def save_guarded_account_session_data(state_machine_data: Tuple[str, dict, Account, Session]):
|
||||
"""
|
||||
:param state_machine_data:
|
||||
:type state_machine_data:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
user_input, ussd_session, account, session = state_machine_data
|
||||
session_data = ussd_session.get('data') or {}
|
||||
guarded_account_phone_number = process_phone_number(phone_number=user_input, region=E164Format.region)
|
||||
session_data['guarded_account_phone_number'] = guarded_account_phone_number
|
||||
save_session_data('cic-ussd', session, session_data, ussd_session)
|
||||
|
||||
|
||||
def retrieve_person_metadata(state_machine_data: Tuple[str, dict, Account, Session]):
|
||||
"""
|
||||
:param state_machine_data:
|
||||
:type state_machine_data:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
user_input, ussd_session, account, session = state_machine_data
|
||||
guardian_phone_number = process_phone_number(user_input, E164Format.region)
|
||||
guardian = Account.get_by_phone_number(guardian_phone_number, session)
|
||||
blockchain_address = guardian.blockchain_address
|
||||
s_query_person_metadata = celery.signature(
|
||||
'cic_ussd.tasks.metadata.query_person_metadata', [blockchain_address], queue='cic-ussd')
|
||||
s_query_person_metadata.apply_async()
|
||||
|
||||
|
||||
def is_valid_guardian_addition(state_machine_data: Tuple[str, dict, Account, Session]):
|
||||
"""
|
||||
:param state_machine_data:
|
||||
:type state_machine_data:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
user_input, ussd_session, account, session = state_machine_data
|
||||
try:
|
||||
phone_number = process_phone_number(user_input, E164Format.region)
|
||||
except NumberParseException:
|
||||
phone_number = None
|
||||
|
||||
preferred_language = get_cached_preferred_language(account.blockchain_address)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
|
||||
is_valid_account = Account.get_by_phone_number(phone_number, session) is not None
|
||||
is_initiator = phone_number == account.phone_number
|
||||
is_existent_guardian = phone_number in account.get_guardians()
|
||||
|
||||
failure_reason = ''
|
||||
if not is_valid_account:
|
||||
failure_reason = translation_for('helpers.error.no_matching_account', preferred_language)
|
||||
|
||||
if is_initiator:
|
||||
failure_reason = translation_for('helpers.error.is_initiator', preferred_language)
|
||||
|
||||
if is_existent_guardian:
|
||||
failure_reason = translation_for('helpers.error.is_existent_guardian', preferred_language)
|
||||
|
||||
if failure_reason:
|
||||
session_data = ussd_session.get('data') or {}
|
||||
session_data['failure_reason'] = failure_reason
|
||||
save_session_data('cic-ussd', session, session_data, ussd_session)
|
||||
|
||||
return phone_number is not None and is_valid_account and not is_existent_guardian and not is_initiator
|
||||
|
||||
|
||||
def add_pin_guardian(state_machine_data: Tuple[str, dict, Account, Session]):
|
||||
"""
|
||||
:param state_machine_data:
|
||||
:type state_machine_data:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
user_input, ussd_session, account, session = state_machine_data
|
||||
guardian_phone_number = ussd_session.get('data').get('guardian_phone_number')
|
||||
account.add_guardian(guardian_phone_number)
|
||||
session.add(account)
|
||||
session.flush()
|
||||
SessionBase.release_session(session=session)
|
||||
|
||||
|
||||
def is_set_pin_guardian(account: Account, checked_number: str, preferred_language: str, session: Session, ussd_session: dict):
|
||||
""""""
|
||||
failure_reason = ''
|
||||
set_guardians = []
|
||||
if account:
|
||||
set_guardians = account.get_guardians()
|
||||
else:
|
||||
failure_reason = translation_for('helpers.error.no_matching_account', preferred_language)
|
||||
|
||||
is_set_guardian = checked_number in set_guardians
|
||||
is_initiator = checked_number == account.phone_number
|
||||
|
||||
if not is_set_guardian:
|
||||
failure_reason = translation_for('helpers.error.is_not_existent_guardian', preferred_language)
|
||||
|
||||
if is_initiator:
|
||||
failure_reason = translation_for('helpers.error.is_initiator', preferred_language)
|
||||
|
||||
if failure_reason:
|
||||
session_data = ussd_session.get('data') or {}
|
||||
session_data['failure_reason'] = failure_reason
|
||||
save_session_data('cic-ussd', session, session_data, ussd_session)
|
||||
|
||||
return is_set_guardian and not is_initiator
|
||||
|
||||
|
||||
def is_dialers_pin_guardian(state_machine_data: Tuple[str, dict, Account, Session]):
|
||||
user_input, ussd_session, account, session = state_machine_data
|
||||
phone_number = process_phone_number(phone_number=user_input, region=E164Format.region)
|
||||
preferred_language = get_cached_preferred_language(account.blockchain_address)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
return is_set_pin_guardian(account, phone_number, preferred_language, session, ussd_session)
|
||||
|
||||
|
||||
def is_others_pin_guardian(state_machine_data: Tuple[str, dict, Account, Session]):
|
||||
user_input, ussd_session, account, session = state_machine_data
|
||||
preferred_language = get_cached_preferred_language(account.blockchain_address)
|
||||
phone_number = process_phone_number(phone_number=user_input, region=E164Format.region)
|
||||
guarded_account = Account.get_by_phone_number(phone_number, session)
|
||||
if not preferred_language:
|
||||
preferred_language = i18n.config.get('fallback')
|
||||
return is_set_pin_guardian(guarded_account, account.phone_number, preferred_language, session, ussd_session)
|
||||
|
||||
|
||||
def remove_pin_guardian(state_machine_data: Tuple[str, dict, Account, Session]):
|
||||
"""
|
||||
:param state_machine_data:
|
||||
:type state_machine_data:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
user_input, ussd_session, account, session = state_machine_data
|
||||
guardian_phone_number = ussd_session.get('data').get('guardian_phone_number')
|
||||
account.remove_guardian(guardian_phone_number)
|
||||
session.add(account)
|
||||
session.flush()
|
||||
SessionBase.release_session(session=session)
|
||||
|
||||
|
||||
def initiate_pin_reset(state_machine_data: Tuple[str, dict, Account, Session]):
|
||||
"""
|
||||
:param state_machine_data:
|
||||
:type state_machine_data:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
user_input, ussd_session, account, session = state_machine_data
|
||||
session_data = ussd_session.get('data')
|
||||
quorum_count = session_data['quorum_count'] if session_data.get('quorum_count') else 0
|
||||
quorum_count += 1
|
||||
session_data['quorum_count'] = quorum_count
|
||||
save_session_data('cic-ussd', session, session_data, ussd_session)
|
||||
guarded_account_phone_number = session_data.get('guarded_account_phone_number')
|
||||
guarded_account = Account.get_by_phone_number(guarded_account_phone_number, session)
|
||||
if quorum_count >= guarded_account.guardian_quora:
|
||||
guarded_account.reset_pin(session)
|
||||
logg.debug(f'Reset initiated for: {guarded_account.phone_number}')
|
||||
session_data['quorum_count'] = 0
|
||||
save_session_data('cic-ussd', session, session_data, ussd_session)
|
@ -11,5 +11,7 @@
|
||||
"account_creation_prompt",
|
||||
"exit_successful_transaction",
|
||||
"exit_insufficient_balance",
|
||||
"exit_invalid_guardian_addition",
|
||||
"exit_invalid_guardian_removal",
|
||||
"complete"
|
||||
]
|
16
apps/cic-ussd/states/pin_management_states.json
Normal file
16
apps/cic-ussd/states/pin_management_states.json
Normal file
@ -0,0 +1,16 @@
|
||||
[
|
||||
"pin_management",
|
||||
"reset_guarded_pin",
|
||||
"reset_guarded_pin_authorization",
|
||||
"exit_pin_reset_initiated_success",
|
||||
"exit_not_authorized_for_pin_reset",
|
||||
"guard_pin",
|
||||
"guardian_list_pin_authorization",
|
||||
"guardian_list",
|
||||
"add_guardian",
|
||||
"add_guardian_pin_authorization",
|
||||
"exit_guardian_addition_success",
|
||||
"remove_guardian",
|
||||
"remove_guardian_pin_authorization",
|
||||
"exit_guardian_removal_success"
|
||||
]
|
@ -50,7 +50,7 @@
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "account_management",
|
||||
"dest": "enter_current_pin",
|
||||
"dest": "pin_management",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_five_selected"
|
||||
},
|
||||
{
|
||||
|
119
apps/cic-ussd/transitions/pin_guard_transitions.json
Normal file
119
apps/cic-ussd/transitions/pin_guard_transitions.json
Normal file
@ -0,0 +1,119 @@
|
||||
[
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "guard_pin",
|
||||
"dest": "guardian_list_pin_authorization",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_one_selected"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "guardian_list_pin_authorization",
|
||||
"dest": "guardian_list",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_authorized_pin"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "guardian_list_pin_authorization",
|
||||
"dest": "exit_pin_blocked",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_blocked_pin"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "guard_pin",
|
||||
"dest": "add_guardian",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_two_selected"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "add_guardian",
|
||||
"dest": "add_guardian_pin_authorization",
|
||||
"after": [
|
||||
"cic_ussd.state_machine.logic.pin_guard.save_guardian_to_session_data",
|
||||
"cic_ussd.state_machine.logic.pin_guard.retrieve_person_metadata"
|
||||
],
|
||||
"conditions": "cic_ussd.state_machine.logic.pin_guard.is_valid_guardian_addition"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "add_guardian",
|
||||
"dest": "exit_invalid_guardian_addition",
|
||||
"unless": "cic_ussd.state_machine.logic.pin_guard.is_valid_guardian_addition"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "add_guardian_pin_authorization",
|
||||
"dest": "exit_guardian_addition_success",
|
||||
"after": "cic_ussd.state_machine.logic.pin_guard.add_pin_guardian",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_authorized_pin"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "add_guardian_pin_authorization",
|
||||
"dest": "exit_pin_blocked",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_locked_account"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "guard_pin",
|
||||
"dest": "remove_guardian",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_three_selected"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "remove_guardian",
|
||||
"dest": "remove_guardian_pin_authorization",
|
||||
"after": [
|
||||
"cic_ussd.state_machine.logic.pin_guard.save_guardian_to_session_data",
|
||||
"cic_ussd.state_machine.logic.pin_guard.retrieve_person_metadata"
|
||||
],
|
||||
"conditions": "cic_ussd.state_machine.logic.pin_guard.is_dialers_pin_guardian"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "remove_guardian",
|
||||
"dest": "exit_invalid_guardian_removal",
|
||||
"unless": "cic_ussd.state_machine.logic.pin_guard.is_dialers_pin_guardian"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "remove_guardian_pin_authorization",
|
||||
"dest": "exit_guardian_removal_success",
|
||||
"after": "cic_ussd.state_machine.logic.pin_guard.remove_pin_guardian",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_authorized_pin"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "remove_guardian_pin_authorization",
|
||||
"dest": "exit_pin_blocked",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_locked_account"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "exit_guardian_removal_success",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_nine_selected"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "exit_invalid_guardian_addition",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_nine_selected"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "exit_invalid_guardian_removal",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_nine_selected"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "guardian_list",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_nine_selected"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "guard_pin",
|
||||
"dest": "exit_invalid_menu_option"
|
||||
}
|
||||
]
|
20
apps/cic-ussd/transitions/pin_management_transitions.json
Normal file
20
apps/cic-ussd/transitions/pin_management_transitions.json
Normal file
@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "pin_management",
|
||||
"dest": "enter_current_pin",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_one_selected"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "pin_management",
|
||||
"dest": "reset_guarded_pin",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_two_selected"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "pin_management",
|
||||
"dest": "guard_pin",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_three_selected"
|
||||
}
|
||||
]
|
43
apps/cic-ussd/transitions/pin_reset_transitions.json
Normal file
43
apps/cic-ussd/transitions/pin_reset_transitions.json
Normal file
@ -0,0 +1,43 @@
|
||||
[
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "reset_guarded_pin",
|
||||
"dest": "reset_guarded_pin_authorization",
|
||||
"after": [
|
||||
"cic_ussd.state_machine.logic.pin_guard.save_guarded_account_session_data",
|
||||
"cic_ussd.state_machine.logic.pin_guard.retrieve_person_metadata"
|
||||
],
|
||||
"conditions": "cic_ussd.state_machine.logic.pin_guard.is_others_pin_guardian"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "reset_guarded_pin_authorization",
|
||||
"dest": "exit_pin_reset_initiated_success",
|
||||
"after": "cic_ussd.state_machine.logic.pin_guard.initiate_pin_reset",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_authorized_pin"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "exit_pin_reset_initiated_success",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_nine_selected"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "reset_guarded_pin_authorization",
|
||||
"dest": "exit_pin_blocked",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_locked_account"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "reset_guarded_pin",
|
||||
"dest": "exit_not_authorized_for_pin_reset",
|
||||
"unless": "cic_ussd.state_machine.logic.pin_guard.is_others_pin_guardian"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "exit_not_authorized_for_pin_reset",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_nine_selected"
|
||||
}
|
||||
]
|
@ -19,3 +19,18 @@ en:
|
||||
Sent
|
||||
to: |-
|
||||
To
|
||||
guardians_list_header: |-
|
||||
Walinzi uliowaongeza ni:
|
||||
no_guardians_list: |-
|
||||
No guardians set
|
||||
error:
|
||||
no_phone_number_provided: |-
|
||||
No phone number was provided.
|
||||
no_matching_account: |-
|
||||
The number provided is not registered.
|
||||
is_initiator: |-
|
||||
Phone number cannot be your own.
|
||||
is_existent_guardian: |-
|
||||
This phone number is is already added as a guardian.
|
||||
is_not_existent_guardian: |-
|
||||
Phone number not set as PIN reset guardian.
|
@ -19,3 +19,18 @@ sw:
|
||||
Ulituma
|
||||
to: |-
|
||||
Kwa
|
||||
guardians_list_header: |-
|
||||
Your set guardians are:
|
||||
no_guardians_list: |-
|
||||
Hamna walinzi walioongezwa
|
||||
error:
|
||||
no_phone_number_provided: |-
|
||||
Namabari ya simu haijawekwa.
|
||||
no_matching_account: |-
|
||||
Nambari uliyoweka haijasajiliwa.
|
||||
is_initiator: |-
|
||||
Nambari yafaa kuwa tofauti na yako.
|
||||
is_existent_guardian: |-
|
||||
Namabari hii tayari imeongezwa kama mlinzi wa nambari ya siri.
|
||||
is_not_existent_guardian: |-
|
||||
Nambari hii haijaongezwa kama mlinzi wa nambari ya siri.
|
@ -70,7 +70,7 @@ en:
|
||||
2. Change language
|
||||
3. Check balance
|
||||
4. Check statement
|
||||
5. Change PIN
|
||||
5. PIN options
|
||||
0. Back
|
||||
metadata_management: |-
|
||||
CON My profile
|
||||
@ -96,6 +96,13 @@ en:
|
||||
0. Back
|
||||
retry_pin_entry: |-
|
||||
CON Incorrect PIN entered, please try again. You have %{remaining_attempts} attempts remaining.
|
||||
0. Back
|
||||
pin_management: |-
|
||||
CON Pin options
|
||||
1. Change PIN
|
||||
2. Reset PIN
|
||||
3. Guard PIN
|
||||
0. Back
|
||||
enter_current_pin:
|
||||
first: |-
|
||||
CON Enter current PIN.
|
||||
@ -108,6 +115,73 @@ en:
|
||||
new_pin_confirmation: |-
|
||||
CON Enter your new four number PIN again
|
||||
0. Back
|
||||
reset_guarded_pin: |-
|
||||
CON Enter phone number you are the guardian to reset their pin
|
||||
0. Back
|
||||
reset_guarded_pin_authorization:
|
||||
first: |-
|
||||
CON Enter YOUR pin to confirm %{guarded_account_information}'s reset
|
||||
0. Back
|
||||
retry: |-
|
||||
%{retry_pin_entry}
|
||||
exit_pin_reset_initiated_success: |-
|
||||
CON Success: You have initiated a PIN reset for %{guarded_account_information}
|
||||
0. Back
|
||||
9. Exit
|
||||
exit_not_authorized_for_pin_reset: |-
|
||||
CON Failure: You are not authorized to reset that PIN. You must be a guardian!
|
||||
0. Back
|
||||
9. Exit
|
||||
guard_pin: |-
|
||||
CON Pin guard
|
||||
1. View guardians
|
||||
2. Add guardian
|
||||
3. Remove guardian
|
||||
0. Back
|
||||
guardian_list_pin_authorization:
|
||||
first: |-
|
||||
CON Enter your pin to view set guardians
|
||||
0. Back
|
||||
retry: |-
|
||||
%{retry_pin_entry}
|
||||
guardian_list: |-
|
||||
CON %{guardians_list}
|
||||
0. Back
|
||||
9. Exit
|
||||
add_guardian: |-
|
||||
CON Enter phone number to add as pin reset guardian
|
||||
0. Back
|
||||
add_guardian_pin_authorization:
|
||||
first: |-
|
||||
CON Enter your pin to add %{guardian_information} as your PIN reset guardian
|
||||
0. Back
|
||||
retry: |-
|
||||
%{retry_pin_entry}
|
||||
exit_guardian_addition_success: |-
|
||||
CON Success: %{guardian_information} can now reset your PIN
|
||||
0. Back
|
||||
9. Exit
|
||||
exit_invalid_guardian_addition: |-
|
||||
CON %{error_exit}
|
||||
0. Back
|
||||
9. Exit
|
||||
remove_guardian: |-
|
||||
CON Enter phone number to revoke guardianship:
|
||||
0. Back
|
||||
remove_guardian_pin_authorization:
|
||||
first: |-
|
||||
CON Enter your pin to remove %{guardian_information} as your PIN reset guardian
|
||||
0. Back
|
||||
retry: |-
|
||||
%{retry_pin_entry}
|
||||
exit_guardian_removal_success: |-
|
||||
CON Success: %{guardian_information} PIN reset guardianship is revoked
|
||||
0. Back
|
||||
9. Exit
|
||||
exit_invalid_guardian_removal: |-
|
||||
CON %{error_exit}
|
||||
0. Back
|
||||
9. Exit
|
||||
transaction_pin_authorization:
|
||||
first: |-
|
||||
CON %{recipient_information} will receive %{transaction_amount} %{token_symbol} from %{sender_information}.
|
||||
|
@ -69,7 +69,7 @@ sw:
|
||||
2. Chagua lugha utakayotumia
|
||||
3. Angalia salio
|
||||
4. Angalia taarifa ya matumizi
|
||||
5. Badilisha nambari ya siri
|
||||
5. Mipangilio ya nambari ya siri
|
||||
0. Nyuma
|
||||
metadata_management: |-
|
||||
CON Wasifu wangu
|
||||
@ -95,6 +95,13 @@ sw:
|
||||
0. Nyuma
|
||||
retry_pin_entry: |-
|
||||
CON Nambari uliyoweka si sahihi, jaribu tena. Una majaribio %{remaining_attempts} yaliyobaki.
|
||||
0. Nyuma
|
||||
pin_management: |-
|
||||
CON Pin options
|
||||
1. Badilisha nambari yangu ya siri
|
||||
2. Tuma ombili la kubadilisha nambari ya siri
|
||||
3. Linda nambari ya siri
|
||||
0. Nyuma
|
||||
enter_current_pin:
|
||||
first: |-
|
||||
CON Weka nambari ya siri.
|
||||
@ -107,6 +114,73 @@ sw:
|
||||
new_pin_confirmation: |-
|
||||
CON Weka nambari yako ya siri tena
|
||||
0. Nyuma
|
||||
reset_guarded_pin: |-
|
||||
CON Weka nambari ya simu ili kutuma ombi la kubalisha nambari ya siri.
|
||||
0. Nyuma
|
||||
reset_guarded_pin_authorization:
|
||||
first: |-
|
||||
CON Weka nambari YAKO ya siri ili kudhibitisha ombi la kubadilisha nambari ya siri ya %{guarded_account_information}.
|
||||
0. Nyuma
|
||||
retry: |-
|
||||
%{retry_pin_entry}
|
||||
exit_pin_reset_initiated_success: |-
|
||||
CON Ombi lako la kubadili nambari ya siri ya %{guarded_account_information} limetumwa.
|
||||
0. Nyuma
|
||||
9. Ondoka
|
||||
exit_not_authorized_for_pin_reset: |-
|
||||
CON Huruhusiwi kutuma ombi la kubadilisha nambari ya siri.
|
||||
0. Nyuma
|
||||
9. Ondoka
|
||||
guard_pin: |-
|
||||
CON Linda nambari ya siri
|
||||
1. Walinzi wa namabari ya siri
|
||||
2. Ongeza mlinzi
|
||||
3. Ondoa mlinzi
|
||||
0. Nyuma
|
||||
guardian_list_pin_authorization:
|
||||
first: |-
|
||||
CON Weka nambari yako ya siri ili kuona walinzi uliowaongeza
|
||||
0. Nyuma
|
||||
retry: |-
|
||||
%{retry_pin_entry}
|
||||
guardian_list: |-
|
||||
CON %{guardians_list}
|
||||
0. Nyuma
|
||||
9. Ondoka
|
||||
add_guardian: |-
|
||||
CON Weka nambari ya simu ili kuongeza mlinzi
|
||||
0. Nyuma
|
||||
add_guardian_pin_authorization:
|
||||
first: |-
|
||||
CON Weka nambari YAKO ya siri ili kumwongeza %{guardian_information} kama mlinzi
|
||||
0. Nyuma
|
||||
retry: |-
|
||||
%{retry_pin_entry}
|
||||
exit_guardian_addition_success: |-
|
||||
CON Ombi lako la kumwongeza: %{guardian_information} kama mlinzi limefanikiwa
|
||||
0. Nyuma
|
||||
9. Ondoka
|
||||
exit_invalid_guardian_addition: |-
|
||||
CON %{error_exit}
|
||||
0. Nyuma
|
||||
9. Ondoka
|
||||
remove_guardian: |-
|
||||
CON Weka nambari ya simu ili kuondoa mlinzi
|
||||
0. Nyuma
|
||||
remove_guardian_pin_authorization:
|
||||
first: |-
|
||||
CON Weka nambari YAKO ya siri ili kumwondoa %{guardian_information} kama mlinzi
|
||||
0. Nyuma
|
||||
retry: |-
|
||||
%{retry_pin_entry}
|
||||
exit_guardian_removal_success: |-
|
||||
CON Ombi lako la kumwondoa: %{guardian_information} kama mlinzi limefanikiwa
|
||||
0. Nyuma
|
||||
9. Ondoka
|
||||
exit_invalid_guardian_removal: |-
|
||||
CON %{error_exit}
|
||||
0. Nyuma
|
||||
9. Ondoka
|
||||
transaction_pin_authorization:
|
||||
first: |-
|
||||
CON %{recipient_information} atapokea %{transaction_amount} %{token_symbol} kutoka kwa %{sender_information}.
|
||||
|
Loading…
Reference in New Issue
Block a user