server/api: move validation to service layer

This commit is contained in:
rr- 2016-04-02 10:01:27 +02:00
parent d3d2540b88
commit 06cf8c79d4
5 changed files with 40 additions and 35 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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) '''

View File

@ -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