def check(request): """ Respond to the "/account/check" API call. """ try: # Extract our payload from the request parameters. request_payload = api_helper.process_request(request) if "error" in request_payload: return request_payload['error'] # Check that the required fields have all been supplied. error = api_helper.check_fields(request_payload, ["session_key", "account_id", "update_id"]) if error != None: return error # Get the values we need from the payload. session = request_payload['session'] account_id = request_payload['fields']['account_id'] update_id = request_payload['fields']['update_id'] if not account_helper.is_valid_account_id(account_id): return api_helper.error(request_payload, api_errors.INVALID_ACCOUNT_ID) # Make sure the user is allowed to access the given account. if account_id != None: if session.user.user_id != account_id.get("user_id"): return api_helper.error(request_payload, api_errors.UNAUTHORIZED) # Get the desired account, if it exists. account = account_helper.get_account(account_id) # See if the account has changed since the client last downloaded it. if account != None: changed = (account.update_id != update_id) else: changed = (update_id != 0) # Finally, return the response payload back to the caller. response_payload = {} if changed: response_payload['changed'] = 1 else: response_payload['changed'] = 0 return api_helper.response(request_payload, response_payload) except: traceback.print_exc() return HttpResponseServerError()
def end(request): """ Respond to the "/session/end" API call. """ try: # Extract our payload from the request parameters. request_payload = api_helper.process_request(request) if "error" in request_payload: return request_payload['error'] # Check that the required fields are present. error = api_helper.check_fields(request_payload, required_fields=["session_key"]) if error != None: return error session_key = request_payload['fields']['session_key'] # Find the Session record associated with this session ID. try: session = Session.objects.get(session_key=session_key) except Session.DoesNotExist: return api_helper.error(request_payload, api_errors.INVALID_SESSION) # Delete the session, and return an empty payload back to the caller. session.delete() return api_helper.response(request_payload, {}, update_session=False) except: traceback.print_exc() return HttpResponseServerError()
def new(request): """ Respond to the "/session/new" API call. """ try: # Extract our payload from the request parameters. request_payload = api_helper.process_request(request) if "error" in request_payload: return request_payload['error'] # Check that the required fields are present. error = api_helper.check_fields(request_payload, required_fields=["user_id", "password"]) if error != None: return error user_id = request_payload['fields']['user_id'] password = request_payload['fields']['password'] # Check that the entered user ID and password are correct. try: user = User.objects.get(user_id=user_id) except User.DoesNotExist: return api_helper.error(request_payload, api_errors.UNKNOWN_USER) if password != user.password: return api_helper.error(request_payload, api_errors.UNAUTHORIZED) # Create a new Session record for this session. session = Session() session.session_key = str(uuid.uuid4()) session.request_num = 1 session.client = request_payload['client'] session.user = user session.save() # Finally, return the session key back to the caller. return api_helper.response(request_payload, {'session_key' : session.session_key}) except: traceback.print_exc() return HttpResponseServerError()
def get(request): """ Respond to the "/account/get" API call. """ try: # Extract our payload from the request parameters. request_payload = api_helper.process_request(request) if "error" in request_payload: return request_payload['error'] # Check that the required fields are present. error = api_helper.check_fields(request_payload, required_fields=["session_key", "account_id"]) if error != None: return error session = request_payload['session'] account_id = request_payload['fields']['account_id'] if not account_helper.is_valid_account_id(account_id): return api_helper.error(request_payload, api_errors.INVALID_ACCOUNT_ID) # Make sure the user is allowed to access the given account. if account_id != None: if session.user.user_id != account_id.get("user_id"): return api_helper.error(request_payload, api_errors.UNAUTHORIZED) # Get the desired account, if it exists. account = account_helper.get_account(account_id) # Build our response payload, based on the account's details. We start # with the account record itself. response_payload = {} response_payload['account'] = {} if account != None: response_payload['account']['exists'] = 1 else: response_payload['account']['exists'] = 0 # Add the account's current balance. balance = decimal.Decimal("0.00") if account != None: transactions = account.transaction_set.all().order_by("timestamp") for transaction in transactions: balance = balance + transaction.amount response_payload['account']['balance'] = balance # Add the list of transactions for this account. response_payload['account']['transactions'] = [] if account != None: transactions = account.transaction_set.all().order_by("timestamp") for transaction in transactions: timestamp = transaction.timestamp.isoformat() amount = transaction.amount metadata = transaction.get_metadata() transaction_data = {'timestamp' : timestamp, 'amount' : amount, 'meta_data' : metadata} other_account = transaction.other_account if other_account != None: other_account_id = account_helper.make_account_id( other_account.user_id, other_account.associated_user_id, other_account.suffix) transaction_data['other_account'] = other_account_id response_payload['account']['transactions'].append( transaction_data) # Add the account's metadata. Note that we exclude the password and pin # number, for security reasons. if account != None: metadata = account.get_metadata() else: metadata = {} if "password" in metadata: del metadata['password'] if "pin_number" in metadata: del metadata['pin_number'] response_payload['account']['meta_data'] = metadata # Add the account's policies, taking into account each policy's default # value, the user-specific override (if any), and account-specific # override (if any). policies = {} for policy in Policy.objects.all(): default = policy.get_default() try: override = PolicyUserOverride.objects.get(policy=policy, user=session.user) user_override = override.get_override() except PolicyUserOverride.DoesNotExist: user_override = None if account != None: try: override = \ PolicyAccountOverride.objects.get(policy=policy, account=account) account_override = override.get_override() except PolicyAccountOverride.DoesNotExist: account_override = None else: account_override = None if account_override != None: value = account_override elif user_override != None: value = user_override else: value = default policies[policy.name] = value response_payload['account']['policies'] = policies # Add the account's update ID. if account != None: response_payload['update_id'] = account.update_id else: response_payload['update_id'] = 0 # Finally, return the response payload back to the caller. return api_helper.response(request_payload, response_payload) except: traceback.print_exc() return HttpResponseServerError()
def new(request): """ Respond to the "/user/new" API call. """ try: # Extract our payload from the request parameters. request_payload = api_helper.process_request(request) if "error" in request_payload: return request_payload['error'] # Check that the required fields are present. error = api_helper.check_fields(request_payload, required_fields=["user_id", "password", "pin_number"], optional_fields=["name"]) if error != None: return error user_id = request_payload['fields']['user_id'] password = request_payload['fields']['password'] pin_number = request_payload['fields']['pin_number'] name = request_payload['fields'].get("name", "") # Check that the supplied values are acceptable. if not account_helper.is_valid_user_id(user_id): return api_helper.error(request_payload, api_errors.INVALID_USER_ID) if not account_helper.is_valid_password(password): return api_helper.error(request_payload, api_errors.INVALID_PASSWORD) if not account_helper.is_valid_pin_number(pin_number): return api_helper.error(request_payload, api_errors.INVALID_PIN_NUMBER) # Check that there is no user with this ID already in the system. try: existing_user = User.objects.get(user_id=user_id) except User.DoesNotExist: existing_user = None if existing_user != None: return api_helper.error(request_payload, api_errors.DUPLICATE_USER_ID) # Create the new User record. user = User() user.user_id = user_id user.name = name user.password = password user.pin_number = pin_number user.save() # Finally, return an empty payload back to the caller. return api_helper.response(request_payload, {}) except: traceback.print_exc() return HttpResponseServerError()
def update(request): """ Respond to the "/user/update" API call. """ try: # Extract our payload from the request parameters. request_payload = api_helper.process_request(request) if "error" in request_payload: return request_payload['error'] user = request_payload['session'].user # Check that the required fields are present. error = api_helper.check_fields(request_payload, required_fields=["session_key", "pin_number", "changes"]) if error != None: return error pin_number = request_payload['fields']['pin_number'] changes = request_payload['fields']['changes'] # Extract the various changes the caller wants to apply. if not isinstance(changes, dict): return HttpResponseBadRequest("Invalid parameter: changes") if "name" in changes: new_name = changes['name'] else: new_name = None if "password" in changes: new_password = changes['password'] else: new_password = None if "pin_number" in changes: new_pin_number = changes['pin_number'] else: new_pin_number = None # Check that the entered values are acceptable. if new_name != None: if not account_helper.is_valid_user_name(new_name): return api_helper.error(request_payload, api_errors.INVALID_USER_NAME) if new_password != None: if not account_helper.is_valid_password(new_password): return api_helper.error(request_payload, api_errors.INVALID_PASSWORD) if new_pin_number != None: if not account_helper.is_valid_pin_number(new_pin_number): return api_helper.error(request_payload, api_errors.INVALID_PIN_NUMBER) # Check that the supplied PIN number is correct. if pin_number != user.pin_number: return api_helper.error(request_payload, api_errors.UNAUTHORIZED) # Update the User record with the updated values. if new_name != None: user.name = new_name if new_password != None: user.password = new_password if new_pin_number != None: user.pin_number = new_pin_number user.save() # Finally, return an empty payload back to the caller. return api_helper.response(request_payload, {}) except: traceback.print_exc() return HttpResponseServerError()
def pay(request): """ Respond to the "/pay" API call. """ try: # Extract our payload from the request parameters. request_payload = api_helper.process_request(request) if "error" in request_payload: return request_payload["error"] # Check that the required fields have all been supplied. error = api_helper.check_fields( request_payload, required_fields=["session_key", "from_account_id", "to_account_id", "amount"], optional_fields=["pin_number", "meta_data"], ) if error != None: return error # Get the values we need from the payload. session = request_payload["session"] from_account_id = request_payload["fields"]["from_account_id"] to_account_id = request_payload["fields"]["to_account_id"] amount = decimal.Decimal(request_payload["fields"]["amount"]) pin_number = request_payload["fields"].get("pin_number") meta_data = request_payload["fields"].get("meta_data") # If the "to" account refers to a user alias, call the user alias # resolver to translate from the user alias to the underlying user ID. if isinstance(to_account_id, dict): if "user_alias" in to_account_id: user_id = alias_resolver.resolve(to_account_id["user_alias"]) if user_id == None: return api_helper.error(request_payload, api_errors.UNKNOWN_ACCOUNT_ALIAS) to_account_id["user_id"] = user_id del to_account_id["user_alias"] # Check that the "from" and "to" accounts are both valid. if not account_helper.is_valid_account_id(from_account_id): return api_helper.error(request_payload, api_errors.INVALID_ACCOUNT_ID) if not account_helper.is_valid_account_id(to_account_id): return api_helper.error(request_payload, api_errors.INVALID_ACCOUNT_ID) # Make sure the user is allowed to access the "from" account. if session.user.user_id != from_account_id.get("user_id"): return api_helper.error(request_payload, api_errors.UNAUTHORIZED) # If this transaction requires a PIN number, ensure that the correct # PIN has been supplied. max_payment_without_pin = account_helper.get_account_policy(from_account_id, "max_payment_without_pin") if max_payment_without_pin != None: if amount >= max_payment_without_pin: # We need a PIN number for this payment -> check that the # correct PIN has been supplied. if pin_number == None: return api_helper.error(request_payload, api_errors.PIN_NUMBER_REQUIRED) if pin_number != session.user.pin_number: return api_helper.error(request_payload, api_errors.INVALID_PIN_NUMBER) # Check that the amount isn't too high, or doesn't exceed the daily # transaction limit...eventually. # Check that the account has enough funds to cover the desired # transaction. min_account_balance = account_helper.get_account_policy(from_account_id, "min_account_balance") if min_account_balance == None: min_account_balance = decimal.Decimal("0.00") account_balance = account_helper.get_account_balance(from_account_id) if account_balance - amount < min_account_balance: return api_helper.error(request_payload, api_errors.INSUFFICIENT_FUNDS) # If necessary, create the Account record(s) for the from and to # accounts. from_account = account_helper.get_or_create_account(from_account_id) to_account = account_helper.get_or_create_account(to_account_id) # If we get here, we're ready to make this payment. Create appropriate # Transaction records to transfer the funds from one account to the # other. timestamp = datetime.datetime.now() transaction = Transaction() transaction.account = from_account transaction.other_account = to_account transaction.timestamp = timestamp transaction.type = Transaction.TYPE_PAYMENT transaction.amount = -amount transaction.set_metadata(meta_data) transaction.save() transaction = Transaction() transaction.account = to_account transaction.other_account = from_account transaction.timestamp = timestamp transaction.type = Transaction.TYPE_PAYMENT transaction.amount = amount transaction.set_metadata(meta_data) transaction.save() # Update the 'update_id' values for both accounts, so that the various # clients will know that the accounts have been updated. account_helper.increment_update_id(from_account.id) account_helper.increment_update_id(to_account.id) # Finally, return an empty response payload back to the caller. We # return an empty response (as opposed to an error response) when the # payment was successful. response_payload = {} return api_helper.response(request_payload, response_payload) except: traceback.print_exc() return HttpResponseServerError()
def set(request): """ Respond to the "/policy/set" API call. """ try: # Extract our payload from the request parameters. request_payload = api_helper.process_request(request) if "error" in request_payload: return request_payload['error'] # Check that the required fields are present. error = api_helper.check_fields(request_payload, required_fields=["session_key", "pin_number", "policy", "value"], optional_fields=["account_id"]) if error != None: return error user = request_payload['session'].user pin_number = request_payload['fields']['pin_number'] account_id = request_payload['fields'].get("account_id") policy_name = request_payload['fields']['policy'] policy_value = request_payload['fields']['value'] # Check that the supplied PIN number is correct. if pin_number != user.pin_number: return api_helper.error(request_payload, api_errors.UNAUTHORIZED) # If an account ID was specified, make sure the user is allowed to # access that account. if account_id != None: if user.user_id != account_id.get("user_id"): return api_helper.error(request_payload, api_errors.UNAUTHORIZED) # Get the Policy the user wants to set the override for. try: policy = Policy.objects.get(name=policy_name) except Policy.DoesNotExist: return api_helper.error(request_payload, api_errors.NO_SUCH_POLICY) # Check that the user is allowed to make this policy override for the # given policy. if not account_helper.is_acceptable_policy_override(policy, policy_value): return api_helper.error(request_payload, api_errors.UNACCEPTABLE_POLICY_OVERRIDE) # Create an appropriate policy override record. if account_id != None: # We need to create an account-level override for this policy and # account, deleting the old override if there is one. account = account_helper.get_or_create_account(account_id) PolicyAccountOverride.objects.filter(policy=policy, account=account).delete() override = PolicyAccountOverride() override.policy = policy override.account = account override.set_override(policy_value) override.save() else: # We need to create a user-level override for this policy and user, # deleing the old override if there is one. PolicyUserOverride.objects.filter(policy=policy, user=user).delete() override = PolicyUserOverride() override.policy = policy override.user = user override.set_override(policy_value) override.save() # Finally, return an empty payload back to the caller. return api_helper.response(request_payload, {}) except: traceback.print_exc() return HttpResponseServerError()
def get(request): """ Respond to the "/policy/get" API call. We let the caller retrieve a policy override (either at the user or the individual account level) for a given policy. """ try: # Extract our payload from the request parameters. request_payload = api_helper.process_request(request) if "error" in request_payload: return request_payload['error'] # Check that the required fields are present. error = api_helper.check_fields(request_payload, required_fields=["session_key", "policy"], optional_fields=["account_id"]) if error != None: return error user = request_payload['session'].user account_id = request_payload['fields'].get("account_id") policy_name = request_payload['fields']['policy'] # If an account ID was specified, make sure the user is allowed to # access that account. if account_id != None: if user.user_id != account_id.get("user_id"): return api_helper.error(request_payload, api_errors.UNAUTHORIZED) # Get the Policy the user wants the override for. try: policy = Policy.objects.get(name=policy_name) except Policy.DoesNotExist: return api_helper.error(request_payload, api_errors.NO_SUCH_POLICY) # Get the appropriate override for this policy, if any. if account_id != None: # Retrieve the account-level override for this policy and account, # if any. account = account_helper.get_account(account_id) if account != None: try: override = PolicyAccountOverride.objects.get(policy=policy, account=account) override_value = override.get_override() except PolicyAccountOverride.DoesNotExist: override_value = None else: override_value = None else: # Retrieve the user-level override for this policy and user, if # any. try: override = PolicyUserOverride.objects.get(policy=policy, user=user) override_value = override.get_override() except PolicyUserOverride.DoesNotExist: override_value = None # Finally, return the policy override (if any) back to the caller. response_payload = {} if override_value != None: response_payload['override'] = override_value return api_helper.response(request_payload, response_payload) except: traceback.print_exc() return HttpResponseServerError()