Module: check_mk
Branch: master
Commit: 0b575ddf14222bd6fae3389bebc099fc376289c4
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=0b575ddf14222b…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Tue Oct 30 13:35:25 2012 +0100
Restructured cookie auth cookies (all auth cookies will be invalid
after update -> all users have to login again)
---
ChangeLog | 2 ++
web/htdocs/login.py | 29 ++++++++++++++++++-----------
web/htdocs/wato.py | 14 +++++++++++++-
3 files changed, 33 insertions(+), 12 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 2d7a325..83f7db0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -16,6 +16,8 @@
* Max. number of queued connections on status sockets is configurable now
Multisite:
+ * Restructured cookie auth cookies (all auth cookies will be invalid
+ after update -> all users have to login again)
* Enable automation login with _username= and _secret=, while
_secret is the content of var/check_mk/web/$USER/automation.secret
* FIX: Fixed releasing of locks and livestatus connections when logging out
diff --git a/web/htdocs/login.py b/web/htdocs/login.py
index 1ca86c4..4fc70fb 100644
--- a/web/htdocs/login.py
+++ b/web/htdocs/login.py
@@ -82,25 +82,32 @@ def load_secret():
return secret
+# Load the password serial of the user. This serial identifies the current config
+# state of the user account. If either the password is changed or the account gets
+# locked the serial is increased by WATO and all cookies get invalidated.
+def load_serial(user_id):
+ users = wato.load_users()
+ return users.get(user_id, {}).get('serial', 0)
+
# Generates the hash to be added into the cookie value
-def generate_hash(username, now, pwhash):
+def generate_hash(username, now, serial):
secret = load_secret()
- return md5.md5(username + now + pwhash + secret).hexdigest()
+ return md5.md5(username + now + str(serial) + secret).hexdigest()
def del_auth_cookie():
name = site_cookie_name()
if html.has_cookie(name):
html.del_cookie(name)
-def auth_cookie_value(username, pwhash):
+def auth_cookie_value(username, serial):
now = str(time.time())
- return username + ':' + now + ':' + generate_hash(username, now, pwhash)
+ return username + ':' + now + ':' + generate_hash(username, now, serial)
-def set_auth_cookie(username, pwhash):
- html.set_cookie(site_cookie_name(), auth_cookie_value(username, pwhash))
+def set_auth_cookie(username, serial):
+ html.set_cookie(site_cookie_name(), auth_cookie_value(username, serial))
def get_cookie_value():
- return auth_cookie_value(config.user_id, load_htpasswd()[config.user_id])
+ return auth_cookie_value(config.user_id, load_serial(config.user_id))
def check_auth_cookie(cookie_name):
username, issue_time, cookie_hash = html.cookie(cookie_name, '::').split(':', 2)
@@ -114,10 +121,10 @@ def check_auth_cookie(cookie_name):
users = load_htpasswd()
if not username in users:
raise MKAuthException(_('Username is unknown'))
- pwhash = users[username]
# Validate the hash
- if cookie_hash != generate_hash(username, issue_time, pwhash):
+ serial = load_serial(username)
+ if cookie_hash != generate_hash(username, issue_time, serial):
raise MKAuthException(_('Invalid credentials'))
# Once reached this the cookie is a good one. Renew it!
@@ -126,7 +133,7 @@ def check_auth_cookie(cookie_name):
# b) A logout is requested
if (html.req.myfile != 'logout' or html.has_var('_ajaxid')) \
and cookie_name == site_cookie_name():
- set_auth_cookie(username, pwhash)
+ set_auth_cookie(username, serial)
# Return the authenticated username
return username
@@ -181,7 +188,7 @@ def do_login():
# a) Set the auth cookie
# b) Unset the login vars in further processing
# c) Show the real requested page (No redirect needed)
- set_auth_cookie(username, users[username])
+ set_auth_cookie(username, load_serial(username))
# Use redirects for URLs or simply execute other handlers for
# mulitsite modules
diff --git a/web/htdocs/wato.py b/web/htdocs/wato.py
index b1b9dba..0a255a5 100644
--- a/web/htdocs/wato.py
+++ b/web/htdocs/wato.py
@@ -7805,13 +7805,19 @@ def mode_edit_user(phase):
if password:
new_user["password"] = encrypt_password(password)
+ # Set initial password serial or increase existing
+ if new:
+ new_user["serial"] = 0
+ else:
+ new_user["serial"] += 1
+
# Email address
email = html.var("email").strip()
regex_email = '^[-a-zäöüÄÖÜA-Z0-9_.]+(a)xn--[-a-za-z0-9]+(\-4kba73ab2jc.[a-zA-Z]+)*$'
if email and not re.match(regex_email, email):
raise MKUserError("email", _("'%s' is not a valid email address." % email))
new_user["email"] = email
-
+
# Pager
pager = html.var("pager").strip()
new_user["pager"] = pager
@@ -10875,6 +10881,12 @@ def page_user_profile():
users[config.user_id]['password'] = encrypt_password(password)
+ # Increase serial to invalidate old cookies
+ if 'serial' not in users[config.user_id]:
+ users[config.user_id]['serial'] = 1
+ else:
+ users[config.user_id]['serial'] += 1
+
save_users(users)
success = True
Module: check_mk
Branch: master
Commit: 049d209bc2193762d16e8f6082896d378598cece
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=049d209bc21937…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Thu Nov 15 11:14:48 2012 +0100
userdb/ldap: Added option to add custom vars to contacts.mk or multisite users.mk
---
web/htdocs/userdb.py | 10 ++++++++++
web/htdocs/wato.py | 8 ++++++--
web/plugins/userdb/ldap.py | 34 +++++++++++++++++++++++++++++-----
3 files changed, 45 insertions(+), 7 deletions(-)
diff --git a/web/htdocs/userdb.py b/web/htdocs/userdb.py
index f64e3c7..a216ec0 100644
--- a/web/htdocs/userdb.py
+++ b/web/htdocs/userdb.py
@@ -74,6 +74,16 @@ def locked_attributes(connector_id):
connector = get_connector(connector_id)
return connector.get('locked_attributes', lambda: [])()
+# Returns a list of multisite attributes
+def multisite_attributes(connector_id):
+ connector = get_connector(connector_id)
+ return connector.get('multisite_attributes', lambda: [])()
+
+# Returns a list of non contact attributes
+def non_contact_attributes(connector_id):
+ connector = get_connector(connector_id)
+ return connector.get('non_contact_attributes', lambda: [])()
+
# This is a function needed in WATO and the htpasswd module. This should
# really be modularized one day. Till this day this is a good place ...
def encrypt_password(password, salt = None):
diff --git a/web/htdocs/wato.py b/web/htdocs/wato.py
index 746e3bf..2de411c 100644
--- a/web/htdocs/wato.py
+++ b/web/htdocs/wato.py
@@ -8417,6 +8417,8 @@ def save_users(profiles):
"locked",
"automation_secret",
"language",
+ "serial",
+ "connector",
] + custom_values
# Keys to put into multisite configuration
@@ -8426,19 +8428,21 @@ def save_users(profiles):
"automation_secret",
"alias",
"language",
+ "serial",
+ "connector",
] + custom_values
# Remove multisite keys in contacts.
contacts = dict(
e for e in
- [ (id, split_dict(user, non_contact_keys, False))
+ [ (id, split_dict(user, non_contact_keys + userdb.non_contact_attributes(user.get('connector')), False))
for (id, user)
in profiles.items() ])
# Only allow explicitely defined attributes to be written to multisite config
users = {}
for uid, profile in profiles.items():
- users[uid] = dict([ (p, val) for p, val in profile.items() if p in multisite_keys ])
+ users[uid] = dict([ (p, val) for p, val in profile.items() if p in multisite_keys + userdb.multisite_attributes(user.get('connector'))])
# Check_MK's monitoring contacts
filename = root_dir + "contacts.mk"
diff --git a/web/plugins/userdb/ldap.py b/web/plugins/userdb/ldap.py
index a6f8669..e605426 100644
--- a/web/plugins/userdb/ldap.py
+++ b/web/plugins/userdb/ldap.py
@@ -270,7 +270,7 @@ ldap_attribute_plugins['email'] = {
# gathered from ldap
'convert': ldap_convert_mail,
# User-Attributes to be written by this plugin and will be locked in WATO
- 'set_attributes': [ 'email' ],
+ 'lock_attributes': [ 'email' ],
}
ldap_attribute_plugins['alias'] = {
@@ -278,7 +278,7 @@ ldap_attribute_plugins['alias'] = {
'help': _('Synchronizes the alias of the LDAP user account into Check_MK.'),
'needed_attributes': lambda: ldap_attrs(['cn']),
'convert': lambda user_id, ldap_user, user: ldap_convert_simple(user_id, ldap_user, user, 'alias', 'cn'),
- 'set_attributes': [ 'alias' ],
+ 'lock_attributes': [ 'alias' ],
}
# Checks wether or not the user auth must be invalidated (increasing the serial).
@@ -313,7 +313,13 @@ ldap_attribute_plugins['auth_expire'] = {
'the password has changed in LDAP or the account has been locked.'),
'needed_attributes': lambda: ldap_attrs(['pw_changed']),
'convert': ldap_convert_auth_expire,
- 'set_attributes': [],
+ 'lock_attributes': [],
+ # When a plugin introduces new user attributes, it should declare the output target for
+ # this attribute. It can either be written to the multisites users.mk or the check_mk
+ # contacts.mk to be forwarded to nagios. Undeclared attributes are stored in the check_mk
+ # contacts.mk file.
+ 'multisite_attributes': ['ldap_pw_last_changed'],
+ 'non_contact_attributes': ['ldap_pw_last_changed'],
}
# .----------------------------------------------------------------------.
@@ -401,9 +407,25 @@ def ldap_sync(add_to_changelog, only_username):
def ldap_locked_attributes():
locked = set([ 'password' ]) # This attributes are locked in all cases!
for key in config.ldap_active_plugins:
- locked.update(ldap_attribute_plugins[key]['set_attributes'])
+ locked.update(ldap_attribute_plugins[key]['lock_attributes'])
return list(locked)
+# Calculates the attributes added in this connector which shal be written to
+# the multisites users.mk
+def ldap_multisite_attributes():
+ attrs = set([])
+ for key in config.ldap_active_plugins:
+ attrs.update(ldap_attribute_plugins[key].get('multisite_attributes', []))
+ return list(attrs)
+
+# Calculates the attributes added in this connector which shal NOT be written to
+# the check_mks contacts.mk
+def ldap_non_contact_attributes():
+ attrs = set([])
+ for key in config.ldap_active_plugins:
+ attrs.update(ldap_attribute_plugins[key].get('non_contact_attributes', []))
+ return list(attrs)
+
# Is called on every multisite http request
def ldap_page():
try:
@@ -429,5 +451,7 @@ multisite_user_connectors.append({
# synchronized and the user is enabled in LDAP and disabled
# in Check_MK. When the user is locked in LDAP a login is
# not possible.
- 'locked_attributes': ldap_locked_attributes,
+ 'locked_attributes': ldap_locked_attributes,
+ 'multisite_attributes': ldap_multisite_attributes,
+ 'non_contact_attributes': ldap_multisite_attributes,
})
Module: check_mk
Branch: master
Commit: 266f6bdf0f53013b886117a5d39d4c9b3ee09463
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=266f6bdf0f5301…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Wed Oct 31 09:24:13 2012 +0100
Basic user attributes can now be locked by the user connectors
---
web/htdocs/userdb.py | 16 +++++++++
web/htdocs/wato.py | 74 ++++++++++++++++++++++++++++++++-------
web/plugins/userdb/htpasswd.py | 2 +
3 files changed, 78 insertions(+), 14 deletions(-)
diff --git a/web/htdocs/userdb.py b/web/htdocs/userdb.py
index 09345e4..18ee600 100644
--- a/web/htdocs/userdb.py
+++ b/web/htdocs/userdb.py
@@ -49,6 +49,22 @@ def load_plugins():
def list_user_connectors():
return [ (c['id'], c['title']) for c in multisite_user_connectors ]
+# Returns the connector dictionary
+def get_connector(connector_id):
+ if connector_id is None:
+ connector_id = 'htpasswd'
+ for connector in multisite_user_connectors:
+ if connector['id'] == connector_id:
+ return connector
+
+# Returns a list of locked attributes. If connector is None the htpasswd
+# connector is assumed.
+def locked_attributes(connector_id):
+ for connector in multisite_user_connectors:
+ if connector['id'] == connector_id:
+ return connector.get('locked_attributes', None)
+ return []
+
# This is a function needed in WATO and the htpasswd module. This should
# really be modularized one day. Till this day this is a good place ...
def encrypt_password(password, salt = None):
diff --git a/web/htdocs/wato.py b/web/htdocs/wato.py
index 8c6dd96..e94cc00 100644
--- a/web/htdocs/wato.py
+++ b/web/htdocs/wato.py
@@ -7616,6 +7616,7 @@ def mode_users(phase):
html.write("<table class=data>")
html.write("<tr><th>" + _("Actions") + "<th>"
+ _("Name")
+ + "</th><th>" + _("Connector")
+ "</th><th>" + _("Authentication")
+ "</th><th>" + _("Locked")
+ "</th><th>" + _("Full Name")
@@ -7645,6 +7646,9 @@ def mode_users(phase):
# ID
html.write("<td>%s</td>" % id)
+ # Connector
+ html.write("<td>%s</td>" % userdb.get_connector(user.get('connector'))['title'])
+
# Authentication
if "automation_secret" in user:
auth_method = _("Automation")
@@ -7738,6 +7742,13 @@ def mode_edit_user(phase):
else:
user = users.get(userid, {})
+ # Returns true if an attribute is locked and should be read only. Is only
+ # checked when modifying an existing user
+ # FIXME: Also lock those attributes on form processing
+ locked_attributes = userdb.locked_attributes(user.get('connector'))
+ def is_locked(attr):
+ return not new and attr in locked_attributes
+
# Load data that is referenced - in order to display dropdown
# boxes and to check for validity.
contact_groups = load_group_information().get("contact", {})
@@ -7897,31 +7908,44 @@ def mode_edit_user(phase):
html.write(userid)
html.hidden_field("userid", userid)
+ def lockable_input(name, dflt):
+ if not is_locked(name):
+ html.text_input(name, user.get(name, dflt), size = 50)
+ else:
+ html.write(user.get(name, dflt))
+ html.hidden_field(name, user.get(name, dflt))
+
# Full name
forms.section(_("Full name"))
- html.text_input("alias", user.get("alias", userid), size = 50)
+ lockable_input('alias', userid)
html.help(_("Full name or alias of the user"))
# Email address
forms.section(_("Email address"))
- html.text_input("email", user.get("email", ""), size = 50)
+ lockable_input('email', '')
html.help(_("The email address is optional and is needed "
"if the user is a monitoring contact and receives notifications "
"via Email."))
forms.section(_("Pager address"))
- html.text_input("pager", user.get("pager", ""), size = 50)
+ lockable_input('pager', '')
html.help(_("The pager address is optional "))
+
forms.header(_("Security"))
forms.section(_("Authentication"))
is_automation = user.get("automation_secret", None) != None
html.radiobutton("authmethod", "password", not is_automation,
_("Normal user login with password"))
html.write("<ul><table><tr><td>%s</td><td>" % _("password:"))
- html.password_input("password", autocomplete="off")
- html.write("</td></tr><tr><td>%s</td><td>" % _("repeat:"))
- html.password_input("password2", autocomplete="off")
- html.write(" (%s)" % _("optional"))
+ if not is_locked('password'):
+ html.password_input("password", autocomplete="off")
+ html.write("</td></tr><tr><td>%s</td><td>" % _("repeat:"))
+ html.password_input("password2", autocomplete="off")
+ html.write(" (%s)" % _("optional"))
+ else:
+ html.write('<i>%s</i>' % _('The password can not be changed (It is locked by the user connector).'))
+ html.hidden_field('password', '')
+ html.hidden_field('password2', '')
html.write("</td></tr></table></ul>")
html.radiobutton("authmethod", "secret", is_automation,
_("Automation secret for machine accounts"))
@@ -7947,7 +7971,11 @@ def mode_edit_user(phase):
# Locking
forms.section(_("Disable password"), simple=True)
- html.checkbox("locked", user.get("locked", False), label = _("disable the login to this account"))
+ if not is_locked('locked'):
+ html.checkbox("locked", user.get("locked", False), label = _("disable the login to this account"))
+ else:
+ html.write(user.get("locked", False) and _('Login disabled') or _('Login possible'))
+ html.hidden_field('locked', user.get("locked", False) and '1' or '')
html.help(_("Disabling the password will prevent a user from logging in while "
"retaining the original password. Notifications are not affected "
"by this setting."))
@@ -7957,9 +7985,18 @@ def mode_edit_user(phase):
entries = roles.items()
entries.sort(cmp = lambda a,b: cmp((a[1]["alias"],a[0]), (b[1]["alias"],b[0])))
for role_id, role in entries:
- html.checkbox("role_" + role_id, role_id in user.get("roles", []))
- url = make_link([("mode", "edit_role"), ("edit", role_id)])
- html.write("<a href='%s'>%s</a><br>" % (url, role["alias"]))
+ if not is_locked('roles'):
+ html.checkbox("role_" + role_id, role_id in user.get("roles", []))
+ url = make_link([("mode", "edit_role"), ("edit", role_id)])
+ html.write("<a href='%s'>%s</a><br>" % (url, role["alias"]))
+ else:
+ is_member = role_id in user.get("roles", [])
+ html.hidden_field("role_" + role_id, is_member and '1' or '')
+ if not is_member:
+ html.write('<i>%s</i>' % _('No roles assigned.'))
+ else:
+ url = make_link([("mode", "edit_role"), ("edit", role_id)])
+ html.write("<a href='%s'>%s</a><br>" % (url, role["alias"]))
html.help(_("By assigning roles to a user he obtains permissions. "
"If a user has more than one role, he gets the maximum of all "
"permissions of his roles. "
@@ -7980,9 +8017,18 @@ def mode_edit_user(phase):
for alias, gid in entries:
if not alias:
alias = gid
- html.checkbox("cg_" + gid, gid in user.get("contactgroups", []))
- url = make_link([("mode", "edit_contact_group"), ("edit", gid)])
- html.write(" <a href=\"%s\">%s</a><br>" % (url, alias))
+ if not is_locked('contactgroups'):
+ html.checkbox("cg_" + gid, gid in user.get("contactgroups", []))
+ url = make_link([("mode", "edit_contact_group"), ("edit", gid)])
+ html.write(" <a href=\"%s\">%s</a><br>" % (url, alias))
+ else:
+ is_member = gid in user.get("contactgroups", [])
+ html.hidden_field("cg_" + gid, is_member and '1' or '')
+ if not is_member:
+ html.write('<i>%s</i>' % _('No contact groups assigned.'))
+ else:
+ url = make_link([("mode", "edit_contact_group"), ("edit", gid)])
+ html.write("<a href='%s'>%s</a><br>" % (url, alias))
html.help(_("Contact groups are used to assign monitoring "
"objects to users. If you haven't defined any contact groups yet, "
diff --git a/web/plugins/userdb/htpasswd.py b/web/plugins/userdb/htpasswd.py
index e1f4dde..4ca5a45 100644
--- a/web/plugins/userdb/htpasswd.py
+++ b/web/plugins/userdb/htpasswd.py
@@ -55,6 +55,8 @@
# locked_attributes
# List of user attributes locked for all users attached to this
# connector. Those locked attributes are read-only in WATO.
+# Lockable attributes at the moment:
+# password, locked, roles, contactgroups, alias, email, pager
import crypt
import defaults
Module: check_mk
Branch: master
Commit: 50835a50ba5044761964b839193ab8c4c44276c3
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=50835a50ba5044…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Thu Nov 15 10:51:57 2012 +0100
Store last changed pw as string, not list; Catching all exceptions in page handler by default
---
web/htdocs/userdb.py | 13 ++++++++++++-
web/plugins/userdb/ldap.py | 11 ++++++++---
2 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/web/htdocs/userdb.py b/web/htdocs/userdb.py
index 643c048..f64e3c7 100644
--- a/web/htdocs/userdb.py
+++ b/web/htdocs/userdb.py
@@ -194,8 +194,19 @@ def hook_save(users):
# Hook function can be registered here to execute actions on a "regular" base without
# user triggered action. This hook is called on each page load.
+# Catch all exceptions and log them to apache error log. Let exceptions raise trough
+# when debug mode is enabled.
def hook_page():
for connector in enabled_connectors():
handler = connector.get('page', None)
- if handler:
+ if not handler:
+ continue
+ try:
handler()
+ except:
+ if config.debug:
+ raise
+ else:
+ import traceback
+ html.log('Exception (%s, page handler): %s' %
+ (connector['id'], traceback.format_exc()))
diff --git a/web/plugins/userdb/ldap.py b/web/plugins/userdb/ldap.py
index fec04e0..a6f8669 100644
--- a/web/plugins/userdb/ldap.py
+++ b/web/plugins/userdb/ldap.py
@@ -113,7 +113,12 @@ def ldap_connect():
ldap_connection.protocol_version = config.ldap_connection['version']
ldap_default_bind()
+ except ldap.SERVER_DOWN:
+ ldap_connection = None # Invalidate connection on failure
+ raise MKLDAPException(_('The LDAP connector is unable to connect to the LDAP server.'))
+
except ldap.LDAPError, e:
+ html.write(repr(e))
ldap_connection = None # Invalidate connection on failure
raise MKLDAPException(e)
@@ -290,12 +295,12 @@ def ldap_convert_auth_expire(user_id, ldap_user, user):
# value has been changed.
if 'ldap_pw_last_changed' not in user:
- return {'ldap_pw_last_changed': ldap_user[changed_attr]} # simply store
+ return {'ldap_pw_last_changed': ldap_user[changed_attr][0]} # simply store
# Update data (and invalidate auth) if the attribute has changed
- if user['ldap_pw_last_changed'] != ldap_user[changed_attr]:
+ if user['ldap_pw_last_changed'] != ldap_user[changed_attr][0]:
return {
- 'ldap_pw_last_changed': ldap_user[changed_attr],
+ 'ldap_pw_last_changed': ldap_user[changed_attr][0],
'serial': user.get('serial', 0) + 1,
}
Module: check_mk
Branch: master
Commit: 393b8a0e013c8322a09140a152ce3f86f0e69f29
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=393b8a0e013c83…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Tue Nov 13 14:38:30 2012 +0100
ldap connection: Better error handling, replacing OMD_SITE in distinguished names from config now
---
web/htdocs/valuespec.py | 25 +++++++++++++++
web/plugins/userdb/ldap.py | 46 ++++++++++++++++++++++------
web/plugins/wato/check_mk_configuration.py | 8 ++--
3 files changed, 65 insertions(+), 14 deletions(-)
diff --git a/web/htdocs/valuespec.py b/web/htdocs/valuespec.py
index 1e6ea41..6d43212 100644
--- a/web/htdocs/valuespec.py
+++ b/web/htdocs/valuespec.py
@@ -2112,3 +2112,28 @@ class Transform(ValueSpec):
def validate_value(self, value, varprefix):
self._valuespec.validate_value(self.forth(value), varprefix)
+
+class LDAPDistinguishedName(TextAscii):
+ def __init__(self, **kwargs):
+ TextAscii.__init__(self, **kwargs)
+
+ def validate_value(self, value, varprefix):
+ TextAscii.validate_value(self, value, varprefix)
+
+ if value:
+ import ldap
+ try:
+ dn = ldap.dn.str2dn(value)
+ except ldap.DECODING_ERROR:
+ raise MKUserError(varprefix, _('Unable to parse the given distingushed name.'))
+
+ # At least one DC= must be in distinguished name
+ found_dc = False
+ for part in dn:
+ html.write(repr(part[0]))
+ key, val = part[0][:2]
+ if key.lower() == 'dc':
+ found_dc = True
+
+ if not found_dc:
+ raise MKUserError(varprefix, _('Found no "dc=" (Domain Component).'))
diff --git a/web/plugins/userdb/ldap.py b/web/plugins/userdb/ldap.py
index 9e40175..def1134 100644
--- a/web/plugins/userdb/ldap.py
+++ b/web/plugins/userdb/ldap.py
@@ -24,7 +24,7 @@
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
-import config
+import config, defaults
# FIXME: For some reason mod_python is missing /usr/lib/python2.7/dist-packages
# in sys.path. Therefor the ldap module can not be found. Need to fix this!
@@ -102,21 +102,35 @@ def ldap_connect():
ldap_default_bind()
except ldap.LDAPError, e:
+ ldap_connection = None # Invalidate connection on failure
raise MKLDAPException(e)
+ except Exception:
+ ldap_connection = None # Invalidate connection on failure
+ raise
+
# Bind with the default credentials
def ldap_default_bind():
- if config.ldap_connection['bind']:
- ldap_bind(config.ldap_connection['bind'][0],
- config.ldap_connection['bind'][1])
- else:
- ldap_bind('', '') # anonymous bind
+ try:
+ if config.ldap_connection['bind']:
+ ldap_bind(ldap_dn(config.ldap_connection['bind'][0]),
+ config.ldap_connection['bind'][1], catch = False)
+ else:
+ ldap_bind('', '', catch = False) # anonymous bind
+ except (ldap.INVALID_CREDENTIALS, ldap.INAPPROPRIATE_AUTH):
+ raise MKLDAPException(_('Unable to connect to LDAP server with the configured bind credentials. '
+ 'Plase fix this in the '
+ '<a href="wato.py?mode=edit_configvar&varname=ldap_connection">LDAP '
+ 'connection settings</a>.'))
-def ldap_bind(username, password):
+def ldap_bind(username, password, catch = True):
try:
ldap_connection.simple_bind_s(username, password)
except ldap.LDAPError, e:
- raise MKLDAPException(_('Unable to authenticate with LDAP (%s)' % e))
+ if catch:
+ raise MKLDAPException(_('Unable to authenticate with LDAP (%s)' % e))
+ else:
+ raise
def ldap_search(base, filt = '(objectclass=*)', columns = [], scope = None):
if scope:
@@ -151,12 +165,24 @@ def ldap_attr(key):
def ldap_attrs(keys):
return map(ldap_attr, keys)
+# Returns the given distinguished name template with replaced vars
+def ldap_dn(tmpl):
+ dn = tmpl
+
+ for key, val in [ ('$OMD_SITE$', defaults.omd_site) ]:
+ if val:
+ dn = dn.replace(key, val)
+ else:
+ dn = dn.replace(key, '')
+
+ return dn
+
def get_user_dn(username):
# Check wether or not the user exists in the directory
# It's only ok when exactly one entry is found.
# Returns the DN in this case.
result = ldap_search(
- config.ldap_userspec['user_dn'],
+ ldap_dn(config.ldap_userspec['user_dn']),
'(%s=%s)' % (ldap_attr('user_id'), ldap.filter.escape_filter_chars(username)),
[key],
)
@@ -174,7 +200,7 @@ def ldap_get_users(add_filter = None):
filt = '(&%s%s)' % (filt, add_filter)
result = {}
- for dn, ldap_user in ldap_search(config.ldap_userspec['user_dn'], filt, columns = columns):
+ for dn, ldap_user in ldap_search(ldap_dn(config.ldap_userspec['user_dn']), filt, columns = columns):
user_id = ldap_user[ldap_attr('user_id')][0]
result[user_id] = ldap_user
diff --git a/web/plugins/wato/check_mk_configuration.py b/web/plugins/wato/check_mk_configuration.py
index f6c2c78..0ed6f4a 100644
--- a/web/plugins/wato/check_mk_configuration.py
+++ b/web/plugins/wato/check_mk_configuration.py
@@ -337,7 +337,7 @@ if userdb.connector_enabled('ldap'):
("bind", Optional(
Tuple(
elements = [
- TextAscii(
+ LDAPDistinguishedName(
title = _("Bind DN"),
help = _("Specify the distinguished name to be used to bind to "
"the LDAP directory."),
@@ -365,7 +365,7 @@ if userdb.connector_enabled('ldap'):
),
domain = "multisite",
)
-
+
register_configvar(group,
"ldap_userspec",
Dictionary(
@@ -373,13 +373,13 @@ if userdb.connector_enabled('ldap'):
help = _("This option configures all user related LDAP options. These options "
"are used by the LDAP user connector to find the needed users in the LDAP directory."),
elements = [
- ("user_dn", TextAscii(
+ ("user_dn", LDAPDistinguishedName(
title = _("User Base DN"),
help = _("The base distinguished name to be used when performing user account "
"related queries to the LDAP server."),
size = 80,
)),
- ("group_dn", TextAscii(
+ ("group_dn", LDAPDistinguishedName(
title = _("Group Base DN"),
help = _("The base distinguished name to be used when performing group "
"related queries to the LDAP server."),