diff --git a/MANIFEST.in b/MANIFEST.in index d04e96f..0a550aa 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,5 +3,6 @@ include COPYING include NEWS include ChangeLog include bin/*.1 +include conf/* recursive-include yubiadmin/static * recursive-include yubiadmin/templates * diff --git a/NEWS b/NEWS index b739311..f1de543 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +* Version 0.1.6 (unreleased) + + * Added LDAP settings to YubiAuth. + * Version 0.1.5 (released 2013-06-07) * Fixed YubiAuth user management which sometimes failed due to database diff --git a/conf/logging.conf b/conf/logging.conf new file mode 100644 index 0000000..dc3b251 --- /dev/null +++ b/conf/logging.conf @@ -0,0 +1,21 @@ +[loggers] +keys=root + +[logger_root] +level=INFO +handlers=fileHandler + +[formatters] +keys=formatter + +[handlers] +keys=fileHandler + +[formatter_formatter] +format=[%(levelname)s] %(asctime)s %(name)s: %(message)s +datefmt=%Y-%m-%d %I:%M:%S + +[handler_fileHandler] +class=handlers.WatchedFileHandler +formatter=formatter +args=("/var/log/yubiadmin.log",) diff --git a/setup.py b/setup.py index 836817f..34ab80d 100755 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ from release import release setup( name='yubiadmin', - version='0.1.5', + version='0.1.6', author='Dain Nilsson', author_email='dain@yubico.com', maintainer='Yubico Open Source Maintainers', diff --git a/yubiadmin/apps/auth.py b/yubiadmin/apps/auth.py index 1073d5f..05e230a 100644 --- a/yubiadmin/apps/auth.py +++ b/yubiadmin/apps/auth.py @@ -27,6 +27,7 @@ import os import requests +import imp from wtforms import Form from wtforms.fields import (SelectField, TextField, BooleanField, IntegerField, PasswordField) @@ -39,17 +40,16 @@ from yubiadmin.util.config import (python_handler, python_list_handler, FileConfig) from yubiadmin.util.form import ConfigForm, FileForm, ListField from yubiadmin.apps.dashboard import panel -import logging as log -try: - from yubiauth import YubiAuth -except: - YubiAuth = None -User = None +import logging __all__ = [ 'app' ] +log = logging.getLogger(__name__) + +YUBIAUTH_INSTALLED = imp.find_module('yubiauth') +YubiAuth = None AUTH_CONFIG_FILE = '/etc/yubico/auth/yubiauth.conf' YKVAL_SERVERS = [ @@ -150,24 +150,39 @@ class SecurityForm(ConfigForm): class HSMForm(ConfigForm): legend = 'YubiHSM' - description = 'Settings for the YubiHSM hardware device' + description = 'Settings for the YubiHSM hardware device.' config = auth_config use_hsm = BooleanField( 'Use a YubiHSM', - description='Check this if you have a YubiHSM to be used by YubiAuth.' + description=""" + Check this if you have a YubiHSM to be used by YubiAuth for more + secure local password validation. + """ ) hsm_device = TextField('YubiHSM device') class LDAPForm(ConfigForm): legend = 'LDAP authentication' - descripting = 'Settings for authenticating users against an LDAP server.' + description = """ + Settings for authenticating users against an LDAP server. When LDAP + authentication is used only users that exist on the LDAP server will be + permitted to log in, and password validatoin will be delegated to the LDAP + server. + """ config = auth_config + attrs = { + 'ldap_server': {'class': 'input-xxlarge'}, + 'ldap_bind_dn': {'class': 'input-xxlarge'} + } use_ldap = BooleanField( 'Authenticate users against LDAP', - description='Check this to authenticate users passwords against LDAP.' + description=""" + Check this to authenticate users passwords externally against an LDAP + server. + """ ) ldap_server = TextField('LDAP server URL') ldap_bind_dn = TextField('Bind DN for user authentication') @@ -262,6 +277,11 @@ def using_default_client(): auth_config['client_secret'] == YKVAL_DEFAULT_SECRET +def using_ldap(): + auth_config.read() + return auth_config['use_ldap'] + + class YubiAuthApp(App): """ @@ -271,7 +291,6 @@ class YubiAuthApp(App): """ name = 'auth' - sections = ['general', 'database', 'validation', 'advanced'] priority = 40 @property @@ -280,19 +299,19 @@ class YubiAuthApp(App): @property def sections(self): - if not YubiAuth: - return ['general', 'database', 'validation', 'advanced'] - return ['general', 'database', 'validation', 'users', 'advanced'] + base = ['general', 'database', 'password', 'otp'] + if YUBIAUTH_INSTALLED: + return base + ['users', 'advanced'] + return base + ['advanced'] @property def dash_panels(self): if using_default_client(): yield panel('YubiAuth', 'Using default YubiCloud client!', - '/%s/validation' % self.name, 'danger') + '/%s/otp' % self.name, 'danger') def general(self, request): - return self.render_forms(request, - [SecurityForm(), LDAPForm(), HSMForm()], + return self.render_forms(request, [SecurityForm()], template='auth/general') def reload(self, request): @@ -302,9 +321,9 @@ class YubiAuthApp(App): def database(self, request): return self.render_forms(request, [DatabaseForm()]) - def validation(self, request): + def otp(self, request): """ - Validation Server(s) + OTP Validation """ form = ValidationServerForm() resp = self.render_forms(request, [form]) @@ -320,6 +339,12 @@ class YubiAuthApp(App): }) return resp + def password(self, request): + """ + Password Validation + """ + return self.render_forms(request, [LDAPForm(), HSMForm()]) + def getapikey(self, request): return self.render_forms(request, [GetApiKeyForm()], success_msg= "API Key registered!") @@ -333,9 +358,11 @@ class YubiAuthApp(App): """ Manage Users """ - global User - if User is None: + global YubiAuth, User + if YubiAuth is None: + from yubiauth import YubiAuth as _yubiauth from yubiauth.core.model import User as _user + YubiAuth = _yubiauth User = _user with YubiAuth() as auth: @@ -388,6 +415,11 @@ class SetPasswordForm(Form): self.verify.data = None +class SetPasswordDisabledForm(Form): + legend = 'Change Password' + description = 'Cannot change password when using LDAP for authentication.' + + class AssignYubiKeyForm(Form): legend = 'Assign YubiKey' assign = TextField('Assign YubiKey', @@ -459,10 +491,10 @@ class YubiAuthUsers(CollectionApp): msg = 'YubiKey assigned!' elif request.params.get('unassign', None): msg = 'YubiKey unassigned!' - return self.render_forms(request, - [SetPasswordForm(user.id), - AssignYubiKeyForm(user.id)], - 'auth/user', user=user.data, + pwd_form = SetPasswordDisabledForm() if using_ldap() else \ + SetPasswordForm(user.id) + forms = [pwd_form, AssignYubiKeyForm(user.id)] + return self.render_forms(request, forms, 'auth/user', user=user.data, success_msg=msg) diff --git a/yubiadmin/config.py b/yubiadmin/config.py index b888156..3cc30d7 100644 --- a/yubiadmin/config.py +++ b/yubiadmin/config.py @@ -29,6 +29,8 @@ import sys import os import imp import errno +import logging +import logging.config from yubiadmin import default_settings __all__ = [ @@ -37,9 +39,11 @@ __all__ = [ SETTINGS_FILE = os.getenv('YUBIADMIN_SETTINGS', '/etc/yubico/admin/yubiadmin.conf') +LOG_CONFIG_FILE = os.path.join(os.path.dirname(os.path.abspath(SETTINGS_FILE)), + 'logging.conf') VALUES = { - #Web interface + # Web interface 'USERNAME': 'user', 'PASSWORD': 'pass', 'INTERFACE': 'iface', @@ -61,8 +65,16 @@ try: sys.dont_write_bytecode = True user_settings = imp.load_source('user_settings', SETTINGS_FILE) settings = parse(user_settings, settings) -except IOError, e: +except IOError as e: if not e.errno in [errno.ENOENT, errno.EACCES]: raise e finally: sys.dont_write_bytecode = dont_write_bytecode + +# Set up logging +try: + logging.config.fileConfig(LOG_CONFIG_FILE) +except: + logging.basicConfig(level=logging.INFO) + log = logging.getLogger(__name__) + log.exception("Unable to configure logging. Logging to console.") diff --git a/yubiadmin/util/config.py b/yubiadmin/util/config.py index 0e3e073..0fafe26 100644 --- a/yubiadmin/util/config.py +++ b/yubiadmin/util/config.py @@ -29,7 +29,7 @@ import os import re import errno import csv -import logging as log +import logging from collections import MutableMapping, OrderedDict __all__ = [ @@ -43,6 +43,8 @@ __all__ = [ 'parse_value' ] +log = logging.getLogger(__name__) + PHP_BLOCKS = re.compile('(?ms)<\?php(.*?)\s*\?>') QUOTED_STR = re.compile(r'\s*[\'"](.*)[\'"]\s*') COMMENTS = re.compile(