client+server: switch to yaml config

This commit is contained in:
rr-
2016-04-06 20:38:45 +02:00
parent 19a357611b
commit 55cc7b59e4
18 changed files with 201 additions and 179 deletions

View File

@ -1,5 +1,5 @@
alembic>=0.8.5
configobj>=5.0.6
pyyaml>=3.11
falcon>=0.3.0
psycopg2>=2.6.1
SQLAlchemy>=1.0.12

View File

@ -19,12 +19,12 @@ class PasswordResetApi(BaseApi):
'User %r hasn\'t supplied email. Cannot reset password.' % user_name)
token = auth.generate_authentication_token(user)
url = '%s/password-reset/%s:%s' % (
config.config['basic']['base_url'].rstrip('/'), user.name, token)
config.config['base_url'].rstrip('/'), user.name, token)
mailer.send_mail(
'noreply@%s' % config.config['basic']['name'],
'noreply@%s' % config.config['name'],
user.email,
MAIL_SUBJECT.format(name=config.config['basic']['name']),
MAIL_BODY.format(name=config.config['basic']['name'], url=url))
MAIL_SUBJECT.format(name=config.config['name']),
MAIL_BODY.format(name=config.config['name'], url=url))
return {}
def post(self, context, user_name):

View File

@ -1,13 +1,26 @@
import os
import configobj
import yaml
from szurubooru import errors
def merge(left, right):
for key in right:
if key in left:
if isinstance(left[key], dict) and isinstance(right[key], dict):
merge(left[key], right[key])
elif left[key] != right[key]:
left[key] = right[key]
else:
left[key] = right[key]
return left
class Config(object):
''' INI config parser and container. '''
''' Config parser and container. '''
def __init__(self):
self.config = configobj.ConfigObj('../config.ini.dist')
if os.path.exists('../config.ini'):
self.config.merge(configobj.ConfigObj('../config.ini'))
with open('../config.yaml.dist') as handle:
self.config = yaml.load(handle.read())
if os.path.exists('../config.yaml'):
with open('../config.yaml') as handle:
self.config = merge(self.config, yaml.load(handle.read()))
self._validate()
def __getitem__(self, key):
@ -15,22 +28,25 @@ class Config(object):
def _validate(self):
'''
Check whether config.ini doesn't contain errors that might prove
Check whether config doesn't contain errors that might prove
lethal at runtime.
'''
all_ranks = self['service']['user_ranks']
all_ranks = self['ranks']
for privilege, rank in self['privileges'].items():
if rank not in all_ranks:
raise errors.ConfigError(
'Rank %r for privilege %r is missing from user_ranks' % (
rank, privilege))
'Rank %r for privilege %r is missing' % (rank, privilege))
for rank in ['anonymous', 'admin', 'nobody']:
if rank not in all_ranks:
raise errors.ConfigError(
'Fixed rank %r is missing from user_ranks' % rank)
if self['service']['default_user_rank'] not in all_ranks:
raise errors.ConfigError('Protected rank %r is missing' % rank)
if self['default_rank'] not in all_ranks:
raise errors.ConfigError(
'Default rank %r is missing from user_ranks' % (
self['service']['default_user_rank']))
'Default rank %r is not on the list of known ranks' % (
self['default_rank']))
for key in ['schema', 'host', 'port', 'user', 'pass', 'name']:
if not self['database'][key]:
raise errors.ConfigError(
'Database is not configured: %r is missing' % key)
config = Config() # pylint: disable=invalid-name

View File

@ -9,11 +9,9 @@ class TestPasswordReset(DatabaseTestCase):
def setUp(self):
super().setUp()
config_mock = {
'basic': {
'secret': 'x',
'base_url': 'http://example.com/',
'name': 'Test instance',
},
'secret': 'x',
'base_url': 'http://example.com/',
'name': 'Test instance',
}
self.old_config = config.config
config.config = config_mock

View File

@ -13,9 +13,7 @@ class TestRetrievingUsers(DatabaseTestCase):
'users:view': 'regular_user',
'users:create': 'regular_user',
},
'service': {
'user_ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
},
'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
}
self.old_config = config.config
config.config = config_mock
@ -74,15 +72,11 @@ class TestCreatingUser(DatabaseTestCase):
def setUp(self):
super().setUp()
config_mock = {
'basic': {
'secret': '',
},
'service': {
'user_name_regex': '.{3,}',
'password_regex': '.{3,}',
'user_ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
'default_user_rank': 'regular_user',
},
'secret': '',
'user_name_regex': '.{3,}',
'password_regex': '.{3,}',
'default_rank': 'regular_user',
'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
'privileges': {
'users:create': 'anonymous',
},
@ -146,14 +140,10 @@ class TestUpdatingUser(DatabaseTestCase):
def setUp(self):
super().setUp()
config_mock = {
'basic': {
'secret': '',
},
'service': {
'user_name_regex': '.{3,}',
'password_regex': '.{3,}',
'user_ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
},
'secret': '',
'user_name_regex': '.{3,}',
'password_regex': '.{3,}',
'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
'privileges': {
'users:edit:self:name': 'regular_user',
'users:edit:self:pass': 'regular_user',

View File

@ -6,7 +6,7 @@ from szurubooru import errors
def get_password_hash(salt, password):
''' Retrieve new-style password hash. '''
digest = hashlib.sha256()
digest.update(config.config['basic']['secret'].encode('utf8'))
digest.update(config.config['secret'].encode('utf8'))
digest.update(salt.encode('utf8'))
digest.update(password.encode('utf8'))
return digest.hexdigest()
@ -42,7 +42,7 @@ def verify_privilege(user, privilege_name):
'''
Throw an AuthError if the given user doesn't have given privilege.
'''
all_ranks = config.config['service']['user_ranks']
all_ranks = config.config['ranks']
assert privilege_name in config.config['privileges']
assert user.rank in all_ranks
@ -54,6 +54,6 @@ def verify_privilege(user, privilege_name):
def generate_authentication_token(user):
''' Generate nonguessable challenge (e.g. links in password reminder). '''
digest = hashlib.md5()
digest.update(config.config['basic']['secret'].encode('utf8'))
digest.update(config.config['secret'].encode('utf8'))
digest.update(user.password_salt.encode('utf8'))
return digest.hexdigest()

View File

@ -10,7 +10,7 @@ def create_user(name, password, email):
update_name(user, name)
update_password(user, password)
update_email(user, email)
user.rank = config.config['service']['default_user_rank']
user.rank = config.config['default_rank']
user.creation_time = datetime.now()
user.avatar_style = db.User.AVATAR_GRAVATAR
return user
@ -18,7 +18,7 @@ def create_user(name, password, email):
def update_name(user, name):
''' Validate and update user's name. '''
name = name.strip()
name_regex = config.config['service']['user_name_regex']
name_regex = config.config['user_name_regex']
if not re.match(name_regex, name):
raise errors.ValidationError(
'Name must satisfy regex %r.' % name_regex)
@ -26,7 +26,7 @@ def update_name(user, name):
def update_password(user, password):
''' Validate and update user's password. '''
password_regex = config.config['service']['password_regex']
password_regex = config.config['password_regex']
if not re.match(password_regex, password):
raise errors.ValidationError(
'Password must satisfy regex %r.' % password_regex)
@ -43,10 +43,10 @@ def update_email(user, email):
def update_rank(user, rank, authenticated_user):
rank = rank.strip()
available_ranks = config.config['service']['user_ranks']
available_ranks = config.config['ranks']
if not rank in available_ranks:
raise errors.ValidationError(
'Bad rank. Valid ranks: %r' % available_ranks)
'Bad rank %r. Valid ranks: %r' % (rank, available_ranks))
if available_ranks.index(authenticated_user.rank) \
< available_ranks.index(rank):
raise errors.AuthError('Trying to set higher rank than your own')