4
\$\begingroup\$

I've written a couple of functions to check if a consumer of the API should be authenticated to use it or not.

Have I done anything blatantly wrong here?

Config

API_CONSUMERS = [{'name': 'localhost',
                  'host': '12.0.0.1:5000',
                  'api_key': 'Ahth2ea5Ohngoop5'},
                 {'name': 'localhost2',
                  'host': '127.0.0.1:5001',
                  'api_key': 'Ahth2ea5Ohngoop6'}]

Exceptions

class BaseException(Exception):

    def __init__(self, logger, *args, **kwargs):
        super(BaseException, self).__init__(*args, **kwargs)
        logger.info(self.message)


class UnknownHostException(BaseException):
    pass


class MissingHashException(BaseException):
    pass


class HashMismatchException(BaseException):
    pass

Authentication methods

import hashlib
from flask import request
from services.exceptions import (UnknownHostException, MissingHashException,
    HashMismatchException)


def is_authenticated(app):
    """
    Checks that the consumers host is valid, the request has a hash and the
    hash is the same when we excrypt the data with that hosts api key

    Arguments:
    app -- instance of the application
    """
    consumers = app.config.get('API_CONSUMERS')
    host = request.host

    try:
        api_key = next(d['api_key'] for d in consumers if d['host'] == host)
    except StopIteration:
        raise UnknownHostException(
            app.logger, 'Authentication failed: Unknown Host (' + host + ')')

    if not request.headers.get('hash'):
        raise MissingHashException(
            app.logger, 'Authentication failed: Missing Hash (' + host + ')')

    hash = calculate_hash(request.method, api_key)

    if hash != request.headers.get('hash'):
        raise HashMismatchException(
            app.logger, 'Authentication failed: Hash Mismatch (' + host + ')')


    return True


def calculate_hash(method, api_key):
    """
    Calculates the hash using either the url or the request content,
    plus the hosts api key

    Arguments:
    method  -- request method
    api_key -- api key for this host
    """
    if request.method == 'GET':
        data_to_hash = request.base_url + '?' + request.query_string
    elif request.method == 'POST':
        data_to_hash = request.data

    data_to_hash += api_key
    return hashlib.sha1(data_to_hash).hexdigest()
\$\endgroup\$
5
\$\begingroup\$

Exception itself actually inherits from a class called BaseException so:

class BaseException(Exception):

Seems backwards.

\$\endgroup\$
3
\$\begingroup\$

Building on @Drewverlee's answer, I would consider renaming BaseException to APIException, or something of the like.

Also, I don't think passing app as an argument is best practice, I would recommend using the function as a decorator on your endpoints instead:

# Your imports
from application import app # Or whatever your non-relative reference is

def is_authenticated(func):
    def func_wrapper():
        # Your code here without the original def call
    return func_wrapper

You can then use this on each endpoint, adding the @is_authenticated decorator, to ensure the app is properly authenticated.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.