Exemple #1
0
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()
Exemple #2
0
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()
Exemple #3
0
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()
Exemple #4
0
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()
Exemple #5
0
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()
Exemple #6
0
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()
Exemple #7
0
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()
Exemple #8
0
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()
Exemple #9
0
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()