diff --git a/Failures.py b/Failures.py index 1f2a780..5e234b2 100644 --- a/Failures.py +++ b/Failures.py @@ -1,101 +1,131 @@ import logging +''' +Failure messages + +These functions provide for a standard return message for all known +and expected error conditions. + +The return payload is a JSON document and an HTTP error code. The JSON +document includes these elements: + + success - boolean + message - a short message that uniquely identifies the error + code - a distrinct return code that allows the client to act on + then specic error condition encountered. + field - optional data element that identified the name of the + data element involved in the error. + data - optional data element that provides an id or key value + for the data set being processed when the error occurred. + +The HTTP error code indicates whether the request succeeded or failed. If +the call is successful, the service will return a code 200 and a message +of "OK". + +If the request is unsuccessful and the error is due to faulty client data, +return an HTTP error code of 401 if the user is unknown or authentication fails. +Return an HTTP 500 error if the failure is due to an issue within the server, +such as unable to access the back-end database. + +''' def unknown_user_id(id_user): logging.debug('Failures: Unknown user id: %s', id_user) return { - 'success': False, - 'message': 'Unknown user', - 'code': 400, - 'data': id_user - }, 500 + 'success': False, + 'message': 'Unknown user', + 'code': 400, + 'data': id_user + }, 400 def unknown_user_email(email): logging.debug('Failures: Unknown user email: %s', email) return { - 'success': False, - 'message': 'Unknown user', - 'code': 400, - 'data': email - }, 500 + 'success': False, + 'message': 'Unknown user', + 'code': 400, + 'data': email + }, 400 def unknown_user_screen_name(screen_name): logging.debug('Failures: Unknown user by screen name: %s', screen_name) return { - 'success': False, - 'message': 'Unknown user screen name', - 'code': 400, - 'data': screen_name - }, 500 + 'success': False, + 'message': 'Unknown user screen name', + 'code': 400, + 'data': screen_name + }, 400 def email_already_in_use(email): logging.debug('Failures: Email already in use: %s', email) return { - 'success': False, - 'message': 'Email already in use', - 'code': 450, - 'data': email - }, 500 + 'success': False, + 'message': 'Email already in use', + 'code': 450, + 'data': email + }, 400 def email_not_confirmed(email): logging.debug('Failures: Email %s not confirmed', email) return { - 'success': False, - 'message': 'Email not confirmed', - 'code': 430 - }, 401 + 'success': False, + 'message': 'Email not confirmed', + 'code': 430, + 'data': email + }, 401 def user_blocked(email): logging.debug('Failures: User %s blocked', email) return { - 'success': False, - 'message': 'User is blocked', - 'code': 420 - }, 401 + 'success': False, + 'message': 'User is blocked', + 'code': 420, + 'data': email + }, 403 def not_a_number(field, value): logging.error('Failures: Not a valid number: %s -> %s', field, value) return { - 'success': False, - 'message': 'Not a valid number', - 'code': 310, - 'field': field, - 'value': value - }, 400 + 'success': False, + 'message': 'Not a valid number', + 'code': 310, + 'field': field, + 'value': value + }, 400 def passwords_do_not_match(): logging.debug('Failures: Passwords do not match') return { - 'success': False, - 'message': "Password confirm doesn't match", - 'code': 460 - }, 500 + 'success': False, + 'message': "Password confirm doesn't match", + 'code': 460 + }, 400 def password_complexity(): logging.debug('Failures: Password is not complex enough') return { - 'success': False, - 'message': "Password is not complex enough", - 'code': 490 - }, 500 + 'success': False, + 'message': "Password is not complex enough", + 'code': 490 + }, 400 def screen_name_already_in_use(screen_name): logging.debug('Failures: Screen name already in use: %s', screen_name) return { - 'success': False, - 'message': "Screenname already in use", - 'data': screen_name, - 'code': 500 - }, 500 + 'success': False, + 'message': "Screenname already in use", + 'code': 500, + 'data': screen_name + }, 400 def rate_exceeded(time): @@ -106,37 +136,38 @@ def rate_exceeded(time): """ logging.debug('Failures: Rate exceeded') return { - 'success': False, - 'message': 'Insufficient bucket tokens', - 'data': time, - 'code': 470 - }, 500 + 'success': False, + 'message': 'Insufficient bucket tokens', + 'code': 470, + 'data': time + }, 400 def wrong_password(email): logging.debug('Failures: Wrong password for %s', email) return { - 'success': False, - 'message': 'Wrong password', - 'code': 410 + 'success': False, + 'message': 'Wrong password', + 'code': 410, + 'data': email }, 401 def unknown_bucket_type(bucket_type): logging.debug('Failures: Unknown bucket type: %s', bucket_type) return { - 'success': False, - 'message': 'Unknown bucket type', - 'code': 180, - 'data': bucket_type - }, 500 + 'success': False, + 'message': 'Unknown bucket type', + 'code': 180, + 'data': bucket_type + }, 400 def wrong_auth_source(auth_source): logging.debug('Failures: Wrong auth source: %s', auth_source) return { - 'success': False, - 'message': 'Wrong auth source', - 'code': 480, - 'data': auth_source - }, 500 + 'success': False, + 'message': 'Wrong auth source', + 'code': 480, + 'data': auth_source + }, 500 diff --git a/app/AuthToken/controllers.py b/app/AuthToken/controllers.py index 0fb64fa..6887706 100644 --- a/app/AuthToken/controllers.py +++ b/app/AuthToken/controllers.py @@ -39,7 +39,7 @@ def post(self): # Parse numbers try: id_user = int(id_user) - except: + except ValueError: return Failures.not_a_number('idUser', id_user) # Validate user exists, is validated and is not blocked diff --git a/app/Authenticate/controllers.py b/app/Authenticate/controllers.py index 5a315ec..89586ae 100644 --- a/app/Authenticate/controllers.py +++ b/app/Authenticate/controllers.py @@ -15,7 +15,7 @@ authenticate_app = Blueprint('authenticate', __name__, url_prefix='/authenticate') api = Api(authenticate_app) - +# Authenticate a login attempt using local auth class AuthenticateLocalUser(Resource): def post(self): @@ -23,27 +23,28 @@ def post(self): server = request.headers.get('server') email = request.form.get('email') password = request.form.get('password') - #browser = request.form.get('browser') - #ip_address = request.form.get('ipAddress') # Validate required fields validation = Validation() validation.add_required_field('server', server) validation.add_required_field('email', email) validation.add_required_field('password', password) - #validation.add_required_field('browser', browser) - #validation.add_required_field('ipAddress', ip_address) + if not validation.is_valid(): return validation.get_validation_response() # Validate user exists, is validated and is not blocked user = user_services.get_user_by_email(email) + if user is None: return Failures.unknown_user_email(email) + if not user.confirmed: return Failures.email_not_confirmed(email) + if user.blocked: return Failures.user_blocked(email) + if user.auth_source != 'local': return Failures.wrong_auth_source(user.auth_source) diff --git a/app/LocalUser/controllers.py b/app/LocalUser/controllers.py index 4757bbc..d01f1fd 100644 --- a/app/LocalUser/controllers.py +++ b/app/LocalUser/controllers.py @@ -63,6 +63,7 @@ def post(self): if confirm_token is None: # Unknown token return {'success': False, 'code': 510} + if confirm_token.id_user != user.id: # Token is not for this user return {'success': False, 'code': 510} @@ -214,6 +215,9 @@ def get(self, email): if user.auth_source != 'local': return Failures.wrong_auth_source(user.auth_source) + if not user.confirmed: + return Failures.email_not_confirmed(user.email) + success, code, message = user_service.send_password_reset(user.id, server) db.session.commit() diff --git a/app/RateLimiting/controllers.py b/app/RateLimiting/controllers.py index 989e183..75c889e 100644 --- a/app/RateLimiting/controllers.py +++ b/app/RateLimiting/controllers.py @@ -76,12 +76,12 @@ def get(self, bucket_type, id_user, count): # Parse numbers try: id_user = int(id_user) - except: + except ValueError: return Failures.not_a_number('idUser', id_user) try: count = int(count) - except: + except ValueError: return Failures.not_a_number('count', count) # Validate user exists, is validated and is not blocked diff --git a/app/__init__.py b/app/__init__.py index 24fbf6e..ace5287 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -24,11 +24,16 @@ app = Flask(__name__) # Application version (major,minor,patch-level) -version = "1.1.7" +version = "1.1.9" """ Change Log +1.1.9 Update failure module to include missing return elements. + +1.1.8 Fail any attempt to reset an account password is the account + email address has not yet been confirmed. + 1.1.7 Update application logging to separate application events from those logged by the uwsgi servivce