From 06cf8c79d4c255e3f69f1c282ebe81139ac3727a Mon Sep 17 00:00:00 2001 From: rr- Date: Sat, 2 Apr 2016 10:01:27 +0200 Subject: [PATCH] server/api: move validation to service layer --- server/szurubooru/api/user_api.py | 34 +++++----------------- server/szurubooru/app.py | 12 +++++--- server/szurubooru/services/__init__.py | 5 ++-- server/szurubooru/services/errors.py | 7 +++-- server/szurubooru/services/user_service.py | 17 +++++++++++ 5 files changed, 40 insertions(+), 35 deletions(-) diff --git a/server/szurubooru/api/user_api.py b/server/szurubooru/api/user_api.py index 6e1824a..fc71b8a 100644 --- a/server/szurubooru/api/user_api.py +++ b/server/szurubooru/api/user_api.py @@ -1,8 +1,7 @@ ''' Users public API. ''' -import re -import falcon -from szurubooru.services.errors import IntegrityError +import sqlalchemy +from szurubooru.services.errors import IntegrityError, ValidationError from szurubooru.api.base_api import BaseApi def _serialize_user(authenticated_user, user): @@ -20,9 +19,8 @@ def _serialize_user(authenticated_user, user): class UserListApi(BaseApi): ''' API for lists of users. ''' - def __init__(self, config, auth_service, user_service): + def __init__(self, auth_service, user_service): super().__init__() - self._config = config self._auth_service = auth_service self._user_service = user_service @@ -34,42 +32,26 @@ class UserListApi(BaseApi): def post(self, context): ''' Creates a new user. ''' self._auth_service.verify_privilege(context.user, 'users:create') - name_regex = self._config['service']['user_name_regex'] - password_regex = self._config['service']['password_regex'] try: name = context.request['name'] password = context.request['password'] email = context.request['email'].strip() - if not email: - email = None except KeyError as ex: - raise falcon.HTTPBadRequest( - 'Malformed data', 'Field %r not found' % ex.args[0]) - - if not re.match(name_regex, name): - raise falcon.HTTPBadRequest( - 'Malformed data', - 'Name must validate %r expression' % name_regex) - - if not re.match(password_regex, password): - raise falcon.HTTPBadRequest( - 'Malformed data', - 'Password must validate %r expression' % password_regex) + raise ValidationError('Field %r not found.' % ex.args[0]) + user = self._user_service.create_user( + context.session, name, password, email) try: - user = self._user_service.create_user( - context.session, name, password, email) context.session.commit() - except: + except sqlalchemy.exc.IntegrityError: raise IntegrityError('User %r already exists.' % name) return {'user': _serialize_user(context.user, user)} class UserDetailApi(BaseApi): ''' API for individual users. ''' - def __init__(self, config, auth_service, user_service): + def __init__(self, auth_service, user_service): super().__init__() - self._config = config self._auth_service = auth_service self._user_service = user_service diff --git a/server/szurubooru/app.py b/server/szurubooru/app.py index 5868aca..b726425 100644 --- a/server/szurubooru/app.py +++ b/server/szurubooru/app.py @@ -13,10 +13,13 @@ import szurubooru.util class _CustomRequest(falcon.Request): context_type = szurubooru.util.dotdict -def _on_auth_error(ex, req, resp, params): +def _on_auth_error(ex, request, response, params): raise falcon.HTTPForbidden('Authentication error', str(ex)) -def _on_integrity_error(ex, req, resp, params): +def _on_validation_error(ex, request, response, params): + raise falcon.HTTPBadRequest('Validation error', str(ex)) + +def _on_integrity_error(ex, request, response, params): raise falcon.HTTPConflict('Integrity violation', ex.args[0]) def create_app(): @@ -41,8 +44,8 @@ def create_app(): auth_service = szurubooru.services.AuthService(config, password_service) user_service = szurubooru.services.UserService(config, password_service) - user_list = szurubooru.api.UserListApi(config, auth_service, user_service) - user = szurubooru.api.UserDetailApi(config, auth_service, user_service) + user_list = szurubooru.api.UserListApi(auth_service, user_service) + user = szurubooru.api.UserDetailApi(auth_service, user_service) app = falcon.API( request_type=_CustomRequest, @@ -55,6 +58,7 @@ def create_app(): app.add_error_handler(szurubooru.services.AuthError, _on_auth_error) app.add_error_handler(szurubooru.services.IntegrityError, _on_integrity_error) + app.add_error_handler(szurubooru.services.ValidationError, _on_validation_error) app.add_route('/users/', user_list) app.add_route('/user/{user_name}', user) diff --git a/server/szurubooru/services/__init__.py b/server/szurubooru/services/__init__.py index efd5e5f..4b89256 100644 --- a/server/szurubooru/services/__init__.py +++ b/server/szurubooru/services/__init__.py @@ -3,7 +3,8 @@ Middle layer between REST API and database. All the business logic goes here. ''' -from szurubooru.services.auth_service import AuthService, AuthError +from szurubooru.services.auth_service import AuthService from szurubooru.services.user_service import UserService from szurubooru.services.password_service import PasswordService -from szurubooru.services.errors import AuthError, IntegrityError +from szurubooru.services.errors import ( + AuthError, IntegrityError, ValidationError) diff --git a/server/szurubooru/services/errors.py b/server/szurubooru/services/errors.py index 09fbe51..01be594 100644 --- a/server/szurubooru/services/errors.py +++ b/server/szurubooru/services/errors.py @@ -2,8 +2,9 @@ class AuthError(RuntimeError): ''' Generic authentication error ''' - pass class IntegrityError(RuntimeError): - ''' Database integrity error ''' - pass + ''' Database integrity error (e.g. trying to edit nonexisting resource) ''' + +class ValidationError(RuntimeError): + ''' Validation error (e.g. trying to create user with invalid name) ''' diff --git a/server/szurubooru/services/user_service.py b/server/szurubooru/services/user_service.py index d8ce8b6..12f03fb 100644 --- a/server/szurubooru/services/user_service.py +++ b/server/szurubooru/services/user_service.py @@ -1,6 +1,8 @@ ''' Exports UserService. ''' +import re from datetime import datetime +from szurubooru.services.errors import ValidationError from szurubooru.model.user import User class UserService(object): @@ -9,9 +11,24 @@ class UserService(object): def __init__(self, config, password_service): self._config = config self._password_service = password_service + self._name_regex = self._config['service']['user_name_regex'] + self._password_regex = self._config['service']['password_regex'] def create_user(self, session, name, password, email): ''' Creates an user with given parameters and returns it. ''' + + if not re.match(self._name_regex, name): + raise ValidationError( + 'Name must satisfy regex %r.' % self._name_regex) + + if not re.match(self._password_regex, password): + raise ValidationError( + 'Password must satisfy regex %r.' % self._password_regex) + + # prefer nulls to empty strings in the DB + if not email: + email = None + user = User() user.name = name user.password = password