Module: check_mk
Branch: master
Commit: 624d1cd2f29617b635fdae92dd37b938ca9e0f02
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=624d1cd2f29617…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Fri Nov 25 10:51:28 2011 +0100
Implemented new cookie based login mechanism with a fancy login GUI
---
ChangeLog | 3 +-
web/htdocs/css/login.css | 31 ++++++++++++++++++++++++
web/htdocs/htmllib.py | 22 +++++++++++-----
web/htdocs/index.py | 2 +-
web/htdocs/login.py | 59 +++++++++++++++++++++++++++++++++++-----------
5 files changed, 94 insertions(+), 23 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 3b1df7d..bb9e5de 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -30,7 +30,8 @@
Multisite:
* New nifty sidebar snapin "Speed-O-Meter"
- * Implemented logout functionality in OMD environments
+ * Implemented new cookie based login mechanism including a fancy login GUI
+ * Implemented logout functionality for basic auth and the new cookie based auth
* New filter for the (new) state in host/service alerts
* New command for sending custom notifications
* FIX: Fixed encoding problem when opening dashboard
diff --git a/web/htdocs/css/login.css b/web/htdocs/css/login.css
new file mode 100644
index 0000000..ca11956
--- /dev/null
+++ b/web/htdocs/css/login.css
@@ -0,0 +1,31 @@
+#login {
+ margin: 0;
+ height: 100%;
+ width: 100%;
+ text-align:center;
+}
+/*#login #logo {
+ width: 320px;
+ margin: 7em auto;
+ height: 55px;
+ background-repeat: no-repeat;
+ background-image: url("../images/sidebar_top.png");
+}*/
+#login h1 {
+ width: 300px;
+ margin: 3em auto 2em auto;
+}
+#login a {
+ color: #ffffff;
+}
+#login a:hover {
+ text-decoration: none;
+}
+#login #table {
+ width: 320px; margin: 0 auto;
+}
+#login #foot {
+ font-size: 0.8em;
+ font-style: italic;
+ color: #ffffff;
+}
diff --git a/web/htdocs/htmllib.py b/web/htdocs/htmllib.py
index d4b9ff7..a8c9164 100644
--- a/web/htdocs/htmllib.py
+++ b/web/htdocs/htmllib.py
@@ -161,6 +161,7 @@ class html:
self.user_errors = {}
self.focus_object = None
self.global_vars = []
+ self.render_headfoot = True
self.browser_reload = 0
self.browser_redirect = ''
self.events = set([]) # currently used only for sounds
@@ -605,6 +606,9 @@ class html:
def html_foot(self):
self.write("</html>\n")
+ def set_render_headfoot(self, render):
+ self.render_headfoot = render
+
def set_browser_reload(self, secs):
self.browser_reload = secs
@@ -618,7 +622,8 @@ class html:
self.html_head(title, **args)
self.write('<body class="main %s">' %
self.var("_body_class", ""))
self.header_sent = True
- self.top_heading(title)
+ if self.render_headfoot:
+ self.top_heading(title)
def top_heading(self, title):
if type(self.req.user) == str:
@@ -643,10 +648,12 @@ class html:
obj = formname + "." + varname
self.write("<script language=\"javascript\"
type=\"text/javascript\">\n"
"<!--\n"
- "document.%s.focus();\n"
- "document.%s.select();\n"
+ "if(document.%s) {"
+ " document.%s.focus();\n"
+ " document.%s.select();\n"
+ "}\n"
"// -->\n"
- "</script>\n" % (obj, obj))
+ "</script>\n" % (obj, obj, obj))
def bottom_footer(self):
if self.req.header_sent:
@@ -654,12 +661,13 @@ class html:
corner_text = ""
if self.browser_reload:
corner_text += _("refresh: %d secs") % self.browser_reload
- si = self.render_status_icons()
- self.write("<table class=footer><tr>"
+ if self.render_headfoot:
+ si = self.render_status_icons()
+ self.write("<table class=footer><tr>"
"<td class=left>%s</td>"
"<td class=middle></td>"
"<td
class=right>%s</td></tr></table>"
- % (si, corner_text))
+ % (si, corner_text))
def body_end(self):
self.write("</body></html>\n")
diff --git a/web/htdocs/index.py b/web/htdocs/index.py
index b8fbf31..c279a3a 100644
--- a/web/htdocs/index.py
+++ b/web/htdocs/index.py
@@ -251,7 +251,7 @@ def handler(req, profiling = True):
# Is the user set by the webserver? otherwise use the cookie based auth
if not req.user or type(req.user) != str:
- config.auth_type == 'cookie'
+ config.auth_type = 'cookie'
# When not authed tell the browser to ask for the password
req.user = login.check_auth()
if req.user == '':
diff --git a/web/htdocs/login.py b/web/htdocs/login.py
index c1b3418..c9dfd39 100644
--- a/web/htdocs/login.py
+++ b/web/htdocs/login.py
@@ -27,14 +27,21 @@
import defaults, htmllib, config
from lib import *
from mod_python import apache
-import md5crypt, time
+import md5, md5crypt, crypt, time
+# Validate hashes taken from the htpasswd file. This method handles
+# crypt() and md5 hashes. This should be the common cases in the
+# used htpasswd files.
def password_valid(pwhash, password):
- # FIXME: MD5 unterstützen
- import crypt
- #html.write(repr(pwhash + ' ' + crypt.crypt(password, pwhash)))
- return pwhash == crypt.crypt(password, pwhash[:2])
+ if pwhash[:3] == '$1$':
+ salt = pwhash.split('$', 3)[2]
+ return pwhash == md5crypt.md5crypt(password, salt, '$1$')
+ else:
+ #html.write(repr(pwhash + ' ' + crypt.crypt(password, pwhash)))
+ return pwhash == crypt.crypt(password, pwhash[:2])
+# Loads the contents of a valid htpasswd file into a dictionary
+# and returns the dictionary
def load_htpasswd():
creds = {}
@@ -44,12 +51,22 @@ def load_htpasswd():
return creds
+# Reads the auth secret from a file. Creates the files if it does
+# not exist. Having access to the secret means that one can issue valid
+# cookies for the cookie auth.
+# FIXME: Secret auch replizieren
+def load_secret():
+ secret_path = '%s/auth.secret' % os.path.dirname(defaults.htpasswd_file)
+ if not os.path.exists(secret_path):
+ secret = md5.md5(str(time.time())).hexdigest()
+ file(secret_path, 'w').write(secret)
+ else:
+ secret = file(secret_path).read().strip()
+ return secret
+
+# Generates the hash to be added into the cookie value
def generate_hash(username, now, pwhash):
- # FIXME: Secret aus Datei auslesen
- # dirname(defaults.htpasswd_file)
- # FIXME: Datei automatisch erstellen, wenn nicht vorhanden
- # FIXME: Secret auch replizieren
- secret = 'asd'
+ secret = load_secret()
return md5crypt.md5crypt(username + now + pwhash, secret)
def del_auth_cookie():
@@ -63,9 +80,13 @@ def set_auth_cookie(username, pwhash):
+ ':' + generate_hash(username, now, pwhash))
def check_auth_cookie():
- username, time, cookie_hash = html.cookie('auth_secret',
'').split(':', 2)
+ username, issue_time, cookie_hash = html.cookie('auth_secret',
'').split(':', 2)
# FIXME: Ablauf-Zeit des Cookies testen
+ #max_cookie_age = 10
+ #if float(issue_time) < time.time() - max_cookie_age:
+ # del_auth_cookie()
+ # return ''
users = load_htpasswd()
if not username in users:
@@ -73,7 +94,7 @@ def check_auth_cookie():
pwhash = users[username]
# Validate the hash
- if cookie_hash != generate_hash(username, time, pwhash):
+ if cookie_hash != generate_hash(username, issue_time, pwhash):
raise Exception
# Once reached this the cookie is a good one. Renew it!
@@ -126,12 +147,17 @@ def page():
err = e
html.add_user_error(e.varname, e.message)
+ html.set_render_headfoot(False)
+
html.header(_("Check_MK Multisite Login"), add_js = False)
if err:
html.write('<div class=error>%s</div>\n' % e.message)
- html.write("<table id=table_login>")
+ html.write("<div id=login>")
+ html.write("<div id=logo></div>")
+ html.write("<h1>Check_MK Multisite</h2>")
+ html.write("<table id=table>")
html.write("<tr class=form>\n")
html.write("<td>")
html.begin_form("login", method = 'POST', add_transid = False)
@@ -159,7 +185,10 @@ def page():
html.write("</td>")
html.write("</tr>")
- html.write("</table></div>")
+ html.write("</table>")
+ html.write("<div id=foot>Version: %s - © "
+ "<a href=\"http://mathias-kettner.de\">Mathias
Kettner</a></div>" % defaults.check_mk_version)
+ html.write("</div>")
html.set_focus('_username')
html.end_form()
@@ -173,6 +202,8 @@ def check_auth():
return check_auth_cookie()
except Exception, e:
+ if config.debug:
+ raise
return ''
def logout():