server/api: move validation to service layer
This commit is contained in:
parent
d3d2540b88
commit
06cf8c79d4
|
@ -1,8 +1,7 @@
|
||||||
''' Users public API. '''
|
''' Users public API. '''
|
||||||
|
|
||||||
import re
|
import sqlalchemy
|
||||||
import falcon
|
from szurubooru.services.errors import IntegrityError, ValidationError
|
||||||
from szurubooru.services.errors import IntegrityError
|
|
||||||
from szurubooru.api.base_api import BaseApi
|
from szurubooru.api.base_api import BaseApi
|
||||||
|
|
||||||
def _serialize_user(authenticated_user, user):
|
def _serialize_user(authenticated_user, user):
|
||||||
|
@ -20,9 +19,8 @@ def _serialize_user(authenticated_user, user):
|
||||||
|
|
||||||
class UserListApi(BaseApi):
|
class UserListApi(BaseApi):
|
||||||
''' API for lists of users. '''
|
''' API for lists of users. '''
|
||||||
def __init__(self, config, auth_service, user_service):
|
def __init__(self, auth_service, user_service):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._config = config
|
|
||||||
self._auth_service = auth_service
|
self._auth_service = auth_service
|
||||||
self._user_service = user_service
|
self._user_service = user_service
|
||||||
|
|
||||||
|
@ -34,42 +32,26 @@ class UserListApi(BaseApi):
|
||||||
def post(self, context):
|
def post(self, context):
|
||||||
''' Creates a new user. '''
|
''' Creates a new user. '''
|
||||||
self._auth_service.verify_privilege(context.user, 'users:create')
|
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:
|
try:
|
||||||
name = context.request['name']
|
name = context.request['name']
|
||||||
password = context.request['password']
|
password = context.request['password']
|
||||||
email = context.request['email'].strip()
|
email = context.request['email'].strip()
|
||||||
if not email:
|
|
||||||
email = None
|
|
||||||
except KeyError as ex:
|
except KeyError as ex:
|
||||||
raise falcon.HTTPBadRequest(
|
raise ValidationError('Field %r not found.' % ex.args[0])
|
||||||
'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)
|
|
||||||
|
|
||||||
|
user = self._user_service.create_user(
|
||||||
|
context.session, name, password, email)
|
||||||
try:
|
try:
|
||||||
user = self._user_service.create_user(
|
|
||||||
context.session, name, password, email)
|
|
||||||
context.session.commit()
|
context.session.commit()
|
||||||
except:
|
except sqlalchemy.exc.IntegrityError:
|
||||||
raise IntegrityError('User %r already exists.' % name)
|
raise IntegrityError('User %r already exists.' % name)
|
||||||
return {'user': _serialize_user(context.user, user)}
|
return {'user': _serialize_user(context.user, user)}
|
||||||
|
|
||||||
class UserDetailApi(BaseApi):
|
class UserDetailApi(BaseApi):
|
||||||
''' API for individual users. '''
|
''' API for individual users. '''
|
||||||
def __init__(self, config, auth_service, user_service):
|
def __init__(self, auth_service, user_service):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._config = config
|
|
||||||
self._auth_service = auth_service
|
self._auth_service = auth_service
|
||||||
self._user_service = user_service
|
self._user_service = user_service
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,13 @@ import szurubooru.util
|
||||||
class _CustomRequest(falcon.Request):
|
class _CustomRequest(falcon.Request):
|
||||||
context_type = szurubooru.util.dotdict
|
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))
|
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])
|
raise falcon.HTTPConflict('Integrity violation', ex.args[0])
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
|
@ -41,8 +44,8 @@ def create_app():
|
||||||
auth_service = szurubooru.services.AuthService(config, password_service)
|
auth_service = szurubooru.services.AuthService(config, password_service)
|
||||||
user_service = szurubooru.services.UserService(config, password_service)
|
user_service = szurubooru.services.UserService(config, password_service)
|
||||||
|
|
||||||
user_list = szurubooru.api.UserListApi(config, auth_service, user_service)
|
user_list = szurubooru.api.UserListApi(auth_service, user_service)
|
||||||
user = szurubooru.api.UserDetailApi(config, auth_service, user_service)
|
user = szurubooru.api.UserDetailApi(auth_service, user_service)
|
||||||
|
|
||||||
app = falcon.API(
|
app = falcon.API(
|
||||||
request_type=_CustomRequest,
|
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.AuthError, _on_auth_error)
|
||||||
app.add_error_handler(szurubooru.services.IntegrityError, _on_integrity_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('/users/', user_list)
|
||||||
app.add_route('/user/{user_name}', user)
|
app.add_route('/user/{user_name}', user)
|
||||||
|
|
|
@ -3,7 +3,8 @@ Middle layer between REST API and database.
|
||||||
All the business logic goes here.
|
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.user_service import UserService
|
||||||
from szurubooru.services.password_service import PasswordService
|
from szurubooru.services.password_service import PasswordService
|
||||||
from szurubooru.services.errors import AuthError, IntegrityError
|
from szurubooru.services.errors import (
|
||||||
|
AuthError, IntegrityError, ValidationError)
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
class AuthError(RuntimeError):
|
class AuthError(RuntimeError):
|
||||||
''' Generic authentication error '''
|
''' Generic authentication error '''
|
||||||
pass
|
|
||||||
|
|
||||||
class IntegrityError(RuntimeError):
|
class IntegrityError(RuntimeError):
|
||||||
''' Database integrity error '''
|
''' Database integrity error (e.g. trying to edit nonexisting resource) '''
|
||||||
pass
|
|
||||||
|
class ValidationError(RuntimeError):
|
||||||
|
''' Validation error (e.g. trying to create user with invalid name) '''
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
''' Exports UserService. '''
|
''' Exports UserService. '''
|
||||||
|
|
||||||
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from szurubooru.services.errors import ValidationError
|
||||||
from szurubooru.model.user import User
|
from szurubooru.model.user import User
|
||||||
|
|
||||||
class UserService(object):
|
class UserService(object):
|
||||||
|
@ -9,9 +11,24 @@ class UserService(object):
|
||||||
def __init__(self, config, password_service):
|
def __init__(self, config, password_service):
|
||||||
self._config = config
|
self._config = config
|
||||||
self._password_service = password_service
|
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):
|
def create_user(self, session, name, password, email):
|
||||||
''' Creates an user with given parameters and returns it. '''
|
''' 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 = User()
|
||||||
user.name = name
|
user.name = name
|
||||||
user.password = password
|
user.password = password
|
||||||
|
|
Loading…
Reference in New Issue