1
0
mirror of https://github.com/Yubico/yubiadmin.git synced 2025-02-26 23:54:26 +01:00

Re-structured code.

This commit is contained in:
Dain Nilsson 2013-04-26 12:04:13 +02:00
parent f3c6be25e6
commit 7038ebe7fe
8 changed files with 265 additions and 295 deletions

View File

@ -2,7 +2,7 @@ import os
from wsgiref.simple_server import make_server from wsgiref.simple_server import make_server
from webob.dec import wsgify from webob.dec import wsgify
from yubiadmin.util import render from yubiadmin.util.app import render
from yubiadmin.apps import apps from yubiadmin.apps import apps

View File

@ -1,4 +1,5 @@
from yubiadmin.util import App, DBConfigForm from yubiadmin.util.app import App
from yubiadmin.util.form import DBConfigForm
__all__ = [ __all__ = [
'app' 'app'
@ -19,7 +20,8 @@ class YubikeyKsm(App):
""" """
Database Settings Database Settings
""" """
return self.render_forms(request, [ dbform = DBConfigForm('/etc/yubico/ksm/config-db.php',
DBConfigForm('/etc/yubico/ksm/config-db.php')]) dbname='ykksm', dbuser='ykksmreader')
return self.render_forms(request, [dbform])
app = YubikeyKsm() app = YubikeyKsm()

View File

@ -1,15 +1,20 @@
import re import re
from wtforms.fields import IntegerField, StringField, Field import os
from wtforms.widgets import TextInput from wtforms.fields import IntegerField
from wtforms.validators import NumberRange from wtforms.validators import NumberRange
from yubiadmin.util import App, DBConfigForm, ConfigForm, FileConfig from yubiadmin.util.app import App
from yubiadmin.util.config import ValueHandler, FileConfig
from yubiadmin.util.form import ConfigForm, DBConfigForm, ListField
__all__ = [ __all__ = [
'app' 'app'
] ]
COMMENT = re.compile(r'/\*.*?\*/')
VALUE = re.compile(r'\s*[\'"](.*)[\'"]\s*')
def yk_read(varname, prefix='', suffix='', flags=None):
def yk_pattern(varname, prefix='', suffix='', flags=None):
regex = r'(?m)^(?!#)\$baseParams\[\'__YKVAL_%s__\'\]\s*=' \ regex = r'(?m)^(?!#)\$baseParams\[\'__YKVAL_%s__\'\]\s*=' \
'\s*%s(.*?)%s\s*;\s*$' % (varname, prefix, suffix) '\s*%s(.*?)%s\s*;\s*$' % (varname, prefix, suffix)
if flags: if flags:
@ -22,141 +27,76 @@ def yk_write(varname, prefix='', suffix=''):
(varname, prefix, x, suffix) (varname, prefix, x, suffix)
def yk_read_str(varname): def yk_handler(varname, default):
return yk_read(varname, '[\'"]', '[\'"]') return ValueHandler(yk_pattern(varname), yk_write(varname),
default=default)
def yk_write_str(varname): def strip_quotes(value):
return yk_write(varname, '"', '"') match = VALUE.match(value)
if match:
return match.group(1)
return value
def strip_comments(value):
return COMMENT.sub('', value)
def yk_parse_arraystring(value):
value = strip_comments(value).strip()
return [strip_quotes(x) for x in value.split(',')]
def yk_array_handler(varname):
pattern = yk_pattern(varname, 'array\(', '\)', 's')
str_write = yk_write(varname, 'array(' + os.linesep, os.linesep + ')')
writer = lambda xs: str_write((',' + os.linesep)
.join(['\t"%s"' % x for x in xs]))
reader = lambda match: yk_parse_arraystring(match.group(1))
return ValueHandler(pattern, writer, reader, [])
ykval_config = FileConfig(
'/home/dain/yubico/yubiadmin/ykval-config.php',
[
('sync_default', yk_handler('SYNC_DEFAULT_LEVEL', 60)),
('sync_secure', yk_handler('SYNC_SECURE_LEVEL', 40)),
('sync_fast', yk_handler('SYNC_FAST_LEVEL', 1)),
('default_timeout', yk_handler('SYNC_DEFAULT_TIMEOUT', 1)),
('sync_interval', yk_handler('SYNC_INTERVAL', 10)),
('resync_timeout', yk_handler('SYNC_RESYNC_TIMEOUT', 30)),
('old_limit', yk_handler('SYNC_OLD_LIMIT', 10)),
('sync_pool', yk_array_handler('SYNC_POOL'))
]
)
class SyncLevelsForm(ConfigForm): class SyncLevelsForm(ConfigForm):
legend = 'Sync Levels' legend = 'Sync Levels'
config = ykval_config
sync_default = IntegerField('Default', [NumberRange(1, 100)]) sync_default = IntegerField('Default', [NumberRange(1, 100)])
sync_secure = IntegerField('Secure', [NumberRange(1, 100)]) sync_secure = IntegerField('Secure', [NumberRange(1, 100)])
sync_fast = IntegerField('Fast', [NumberRange(1, 100)]) sync_fast = IntegerField('Fast', [NumberRange(1, 100)])
config = FileConfig(
'/home/dain/yubico/yubiadmin/ykval-config.php',
[
(
'sync_default',
yk_read('SYNC_DEFAULT_LEVEL'),
yk_write('SYNC_DEFAULT_LEVEL'),
60
), (
'sync_secure',
yk_read('SYNC_SECURE_LEVEL'),
yk_write('SYNC_SECURE_LEVEL'),
40
), (
'sync_fast',
yk_read('SYNC_FAST_LEVEL'),
yk_write('SYNC_FAST_LEVEL'),
1
),
]
)
class MiscForm(ConfigForm): class MiscForm(ConfigForm):
legend = 'Misc' legend = 'Misc'
config = ykval_config
default_timeout = IntegerField('Default Timeout', [NumberRange(0)]) default_timeout = IntegerField('Default Timeout', [NumberRange(0)])
config = FileConfig(
'/home/dain/yubico/yubiadmin/ykval-config.php',
[(
'default_timeout',
yk_read('SYNC_DEFAULT_TIMEOUT'),
yk_write('SYNC_DEFAULT_TIMEOUT'),
1
)]
)
class ListField(Field):
COMMENT = re.compile(r'/\*.*?\*/')
VALUE = re.compile(r'\s*[\'"](.*)[\'"]\s*')
widget = TextInput()
def process_formdata(self, values):
if values:
self.data = filter(None, [x.strip() for x in values[0].split(',')])
def process_data(self, value):
if value:
data = []
value = self.COMMENT.sub('', value)
for val in value.split(','):
match = self.VALUE.match(val)
if match:
data.append(match.group(1))
self.data = data
else:
self.data = []
def _value(self):
if self.data:
return ', '.join(self.data)
else:
return ''
def yk_array_write(varname):
str_write = yk_write(varname, 'array(', ')')
return lambda xs: str_write(', '.join(['"%s"' % x for x in xs]))
class SyncPoolForm(ConfigForm): class SyncPoolForm(ConfigForm):
legend = 'Sync Settings' legend = 'Sync Settings'
config = ykval_config
attrs = {'sync_pool': {'rows': 5, 'class': 'input-xlarge'}}
sync_interval = IntegerField('Sync Interval', [NumberRange(1)]) sync_interval = IntegerField('Sync Interval', [NumberRange(1)])
resync_timeout = IntegerField('Resync Timeout', [NumberRange(1)]) resync_timeout = IntegerField('Resync Timeout', [NumberRange(1)])
old_limit = IntegerField('Old Limit', [NumberRange(1)]) old_limit = IntegerField('Old Limit', [NumberRange(1)])
sync_pool = ListField('Servers') sync_pool = ListField('Servers')
sync_pool_add = StringField('Add Server')
config = FileConfig(
'/home/dain/yubico/yubiadmin/ykval-config.php',
[
(
'sync_interval',
yk_read('SYNC_INTERVAL'),
yk_write('SYNC_INTERVAL'),
10
), (
'resync_timeout',
yk_read('SYNC_RESYNC_TIMEOUT'),
yk_write('SYNC_RESYNC_TIMEOUT'),
30
), (
'old_limit',
yk_read('SYNC_OLD_LIMIT'),
yk_write('SYNC_OLD_LIMIT'),
10
), (
'sync_pool',
yk_read('SYNC_POOL', 'array\(', '\)', 's'),
yk_array_write('SYNC_POOL'),
''
)
]
)
def validate(self):
if super(SyncPoolForm, self).validate():
if self.sync_pool_add.data:
self.sync_pool.data.append(self.sync_pool_add.data)
self.sync_pool_add.process_data(None)
return True
return False
COMMENT = re.compile(r'/\*.*?\*/')
def remove_comments(content):
return COMMENT.sub('', content)
class YubikeyVal(App): class YubikeyVal(App):
@ -179,8 +119,9 @@ class YubikeyVal(App):
""" """
Database Settings Database Settings
""" """
return self.render_forms(request, [ dbform = DBConfigForm('/home/dain/yubico/yubiadmin/config-db.php',
DBConfigForm('/home/dain/yubico/yubiadmin/config-db.php')]) dbname='ykval', dbuser='ykval_verifier')
return self.render_forms(request, [dbform])
def syncpool(self, request): def syncpool(self, request):
""" """
@ -189,8 +130,7 @@ class YubikeyVal(App):
sync_pool_form = SyncPoolForm() sync_pool_form = SyncPoolForm()
form_page = self.render_forms(request, [sync_pool_form]) form_page = self.render_forms(request, [sync_pool_form])
print 'Sync pool: %s' % \ print 'Sync pool: %r' % sync_pool_form.config['sync_pool']
remove_comments(sync_pool_form.config['sync_pool'])
return form_page return form_page
def ksms(self, request): def ksms(self, request):

View File

@ -1,171 +0,0 @@
import os
import re
from UserDict import DictMixin
from wtforms import Form, StringField, IntegerField, PasswordField
from wtforms.widgets import PasswordInput
from wtforms.validators import Optional, NumberRange
from jinja2 import Environment, FileSystemLoader
__all__ = [
'App',
'FileConfig',
'ConfigForm',
'DBConfigForm',
'render',
'populate_forms',
]
cwd = os.path.dirname(__file__)
base_dir = os.path.abspath(os.path.join(cwd, os.pardir))
template_dir = os.path.join(base_dir, 'templates')
env = Environment(loader=FileSystemLoader(template_dir))
def render(tmpl, **kwargs):
template = env.get_template('%s.html' % tmpl)
return template.render(**kwargs)
def populate_forms(forms, data):
if not data:
for form in forms:
form.load()
else:
errors = False
for form in forms:
form.process(data)
errors = not form.validate() or errors
if not errors:
for form in forms:
form.save()
else:
print 'Errors!'
class App(object):
name = None
sections = []
def render_forms(self, request, forms):
populate_forms(forms, request.params)
return render('form', target=request.path, fieldsets=forms)
class ValueHandler(object):
def __init__(self, pattern, writer, default=None, group=1):
self.pattern = re.compile(pattern)
self.writer = writer
self.default = default
self.group = group
def read(self, content):
match = self.pattern.search(content)
if match:
return match.group(self.group)
return self.default
def write(self, content, value):
if value is None:
value = ''
if self.pattern.search(content):
content = self.pattern.sub(self.writer(value), content, 1)
else:
content += os.linesep + self.writer(value)
return content
class FileConfig(DictMixin):
"""
Maps key-value pairs to a backing config file.
You can manually edit the file by modifying self.content.
"""
def __init__(self, filename, params=[]):
self.filename = filename
self.params = {}
for param in params:
self.add_param(*param)
def read(self):
try:
with open(self.filename, 'r') as file:
self.content = file.read()
except IOError as e:
print e
self.content = ''
def commit(self):
with open(self.filename, 'w+') as file:
file.write(self.content)
def add_param(self, key, pattern, writer, default=None, group=1):
self.params[key] = ValueHandler(pattern, writer, default, group)
def __getitem__(self, key):
return self.params[key].read(self.content)
def __setitem__(self, key, value):
self.content = self.params[key].write(self.content, value)
def keys(self):
return self.params.keys()
def __delitem__(self, key):
del self.params[key]
class ConfigForm(Form):
"""
Form that can load and save data to a config.
"""
config = None
def load(self):
self.config.read()
for field in self:
if field.id in self.config:
field.process_data(self.config[field.id])
def save(self):
self.config.read()
for field in self:
if field.id in self.config:
self.config[field.id] = field.data
self.config.commit()
def db_read(varname):
return r'\$db%s=\'(.*)\';' % varname
def db_write(varname):
return lambda x: '$db%s=\'%s\';' % (varname, x)
class DBConfigForm(ConfigForm):
"""
Complete form for editing a dbconfig-common generated for PHP.
"""
legend = 'Database'
dbtype = StringField('DB type')
dbserver = StringField('Host')
dbport = IntegerField('Port', [Optional(), NumberRange(1, 65535)])
dbname = StringField('DB name')
dbuser = StringField('DB username')
dbpass = PasswordField('DB password',
widget=PasswordInput(hide_value=False))
config = FileConfig(
'/dev/null',
[
('dbtype', db_read('type'), db_write('type'), 'mysql'),
('dbserver', db_read('server'), db_write('server'), 'localhost'),
('dbport', db_read('port'), db_write('port'), ''),
('dbname', db_read('name'), db_write('name'), 'ykval'),
('dbuser', db_read('user'), db_write('user'), 'ykval_verifier'),
('dbpass', db_read('pass'), db_write('pass'), ''),
]
)
def __init__(self, filename, *args, **kwargs):
self.__class__.config.filename = filename
super(DBConfigForm, self).__init__(*args, **kwargs)

View File

43
yubiadmin/util/app.py Normal file
View File

@ -0,0 +1,43 @@
import os
from jinja2 import Environment, FileSystemLoader
__all__ = [
'App',
'render',
'populate_forms',
]
cwd = os.path.dirname(__file__)
base_dir = os.path.abspath(os.path.join(cwd, os.pardir, os.pardir))
template_dir = os.path.join(base_dir, 'templates')
env = Environment(loader=FileSystemLoader(template_dir))
def render(tmpl, **kwargs):
template = env.get_template('%s.html' % tmpl)
return template.render(**kwargs)
def populate_forms(forms, data):
if not data:
for form in forms:
form.load()
else:
errors = False
for form in forms:
form.process(data)
errors = not form.validate() or errors
if not errors:
for form in forms:
form.save()
else:
print 'Errors!'
class App(object):
name = None
sections = []
def render_forms(self, request, forms, template='form'):
populate_forms(forms, request.params)
return render(template, target=request.path, fieldsets=forms)

71
yubiadmin/util/config.py Normal file
View File

@ -0,0 +1,71 @@
import os
import re
from UserDict import DictMixin
__all__ = [
'ValueHandler',
'FileConfig'
]
class ValueHandler(object):
def __init__(self, pattern, writer, reader=lambda x: x.group(1),
default=None):
self.pattern = re.compile(pattern)
self.writer = writer
self.reader = reader
self.default = default
def read(self, content):
match = self.pattern.search(content)
if match:
return self.reader(match)
return self.default
def write(self, content, value):
if value is None:
value = ''
if self.pattern.search(content):
content = self.pattern.sub(self.writer(value), content, 1)
else:
content += os.linesep + self.writer(value)
return content
class FileConfig(DictMixin):
"""
Maps key-value pairs to a backing config file.
You can manually edit the file by modifying self.content.
"""
def __init__(self, filename, params=[]):
self.filename = filename
self.params = {}
for param in params:
self.add_param(*param)
def read(self):
try:
with open(self.filename, 'r') as file:
self.content = file.read()
except IOError as e:
print e
self.content = ''
def commit(self):
with open(self.filename, 'w+') as file:
file.write(self.content)
def add_param(self, key, handler):
self.params[key] = handler
def __getitem__(self, key):
return self.params[key].read(self.content)
def __setitem__(self, key, value):
self.content = self.params[key].write(self.content, value)
def keys(self):
return self.params.keys()
def __delitem__(self, key):
del self.params[key]

85
yubiadmin/util/form.py Normal file
View File

@ -0,0 +1,85 @@
from wtforms import Form, StringField, IntegerField, PasswordField, Field
from wtforms.widgets import PasswordInput, TextArea
from wtforms.validators import Optional, NumberRange
from yubiadmin.util.config import ValueHandler, FileConfig
__all__ = [
'ListField',
'ConfigForm',
'DBConfigForm'
]
class ListField(Field):
widget = TextArea()
def process_formdata(self, values):
if values:
self.data = filter(None, [x.strip() for x in values[0].split()])
def _value(self):
if self.data:
return '\n'.join(self.data)
else:
return ''
class ConfigForm(Form):
"""
Form that can load and save data to a config.
"""
config = None
def load(self):
self.config.read()
for field in self:
if field.id in self.config:
field.process_data(self.config[field.id])
def save(self):
self.config.read()
for field in self:
if field.id in self.config:
self.config[field.id] = field.data
self.config.commit()
class DBConfigForm(ConfigForm):
"""
Complete form for editing a dbconfig-common generated for PHP.
"""
legend = 'Database'
dbtype = StringField('DB type')
dbserver = StringField('Host')
dbport = IntegerField('Port', [Optional(), NumberRange(1, 65535)])
dbname = StringField('DB name')
dbuser = StringField('DB username')
dbpass = PasswordField('DB password',
widget=PasswordInput(hide_value=False))
def db_handler(self, varname, default):
pattern = r'\$%s=\'(.*)\';' % varname
writer = lambda x: '$%s=\'%s\';' % (varname, x)
return ValueHandler(pattern, writer, default=default)
def __init__(self, filename, *args, **kwargs):
if not self.config:
self.config = FileConfig(
filename,
[
('dbtype', self.db_handler(
'dbtype', kwargs.pop('dbtype', 'mysql'))),
('dbserver', self.db_handler(
'dbserver', kwargs.pop('dbserver', 'localhost'))),
('dbport', self.db_handler(
'dbport', kwargs.pop('dbport', ''))),
('dbname', self.db_handler(
'dbname', kwargs.pop('dbname', ''))),
('dbuser', self.db_handler(
'dbuser', kwargs.pop('dbuser', ''))),
('dbpass', self.db_handler(
'dbpass', kwargs.pop('dbpass', ''))),
]
)
super(DBConfigForm, self).__init__(*args, **kwargs)