Commit 1104939a authored by Lysander Trischler's avatar Lysander Trischler

Introduce API passwords which only work for API requests

parent 06eb649f
......@@ -741,18 +741,95 @@ if __name__ == "__main__":
ORDER BY start DESC, end DESC, count, username
""", start, end)])
@route(r'/passwd')
class ChangeAPIPasswordHandler(BaseHandler):
def use_api_password(self):
return self.db.get("""SELECT NOT ISNULL(api_password) AS use_api_password
FROM users
WHERE username = %s
LIMIT 0, 1""",
self.current_user.username).use_api_password
@authenticated
def get(self):
self.render("passwd.html", use_api_password=self.use_api_password())
@authenticated
def post(self):
if self.get_string_argument("change", None) is not None:
#
# change API password: must be at least 12 chars long
#
password = self.get_string_argument("password", "")
if len(password) < 12:
self.error_messages.append("Das API-Passwort muss mindestens 12 Zeichen"
" lang sein!")
else:
self.db.execute("""UPDATE users
SET api_password = %s
WHERE username = %s
LIMIT 1""",
kraftwerk_api_password_hash(self.current_user.username,
password),
self.current_user.username)
self.success_messages.append("Das API-Passwort wurde erfolgreich geändert,"
" der Zugriff auf die REST-API ist nun sowohl"
" mit dem API-Passwort als auch mit dem"
" IsoBeef-Benutzer-Passwort möglich.")
self.redirect(self.reverse_url("ChangeAPIPasswordHandler"))
return
elif self.get_string_argument("remove", None) is not None:
#
# remove API password: remove checkbox must be activated
#
if self.get_bool_argument("deactivate", False):
self.db.execute("""UPDATE users
SET api_password = NULL
WHERE username = %s
LIMIT 1""",
self.current_user.username)
self.success_messages.append("Das API-Passwort wurde erfolgreich gelöscht,"
" der Zugriff auf die REST-API ist nur noch"
" mit dem IsoBeef-Benutzer-Passwort möglich.")
self.redirect(self.reverse_url("ChangeAPIPasswordHandler"))
return
else:
self.error_messages.append("Zum Löschen des API-Passwortes muss das Häkchen"
" „API-Passwort löschen“ gesetzt sein!")
self.render("passwd.html", use_api_password=self.use_api_password())
@route(r'/api/')
class APIHandler(BaseHandler):
def get(self):
self.render("api.html")
def kraftwerk_api_password_hash(username, password):
return hashlib.sha1("%s:%s" % (username, password)).hexdigest()
def isobeef_ldap_or_kraftwerk_api_password_auth(self, username, password):
user = tornadyse.isobeef_ldap_auth(username, password)
if user is not None:
return user
user = self.db.get("""SELECT username, display_name
FROM users
WHERE username = %s AND api_password = %s
LIMIT 0, 1""",
username,
kraftwerk_api_password_hash(username, password))
if user is not None:
return tornadyse.DummyIsoBeefLDAPUser(**user)
return None
@route(r'/api/exercises')
class APIExercisesHandler(BaseHandler, tornadyse.BasicAuthMixin):
def load_session(self): pass
def save_session(self): pass
@tornadyse.basic_auth(tornadyse.isobeef_ldap_auth)
@tornadyse.basic_auth(isobeef_ldap_or_kraftwerk_api_password_auth)
def get(self):
best_accept = self.get_browser_accept().best("application/json", "text/csv")
exercises = self.db.query("""
......
......@@ -15,6 +15,7 @@
{% module navi("ListUsersHandler", "Benutzerübersicht") %}
{% module navi("ListExercisesHandler", "Übungsübersicht") %}
{% module navi("AddExerciseHandler", "Übung eintragen") %}
{% module navi("ChangeAPIPasswordHandler", "API-Passwort ändern") %}
{% module navi("LogoutHandler", "%s abmelden" % (current_user.display_name or current_user.username)) %}
{% else %}
{% module navi("LoginHandler", "Anmelden") %}
......
{% extends base.html %}
{% block title %}API-Passwort ändern{% end %}
{% block content %}
<section>
<h1>API-Passwort ändern</h1>
<p>Mit dem API-Passwort kann ein anderes Passwort für die
{% module navi("APIHandler", text="REST-API", title="REST-API") %}
verwendet werden. Dieses ist ausschließlich dort gültig und wird bei
der Anmeldung auf der Website abgewiesen. Es dient neben dem eigentlichen
IsoBeef-Benutzer-Passwort als zusätzliche Authentifizierungsmöglichkeit
speziell für die API. So kann dieses in eigenen Clients verwendet werden,
ohne dass dafür das IsoBeef-Benutzer-Passwort dort eingegeben oder gar
gespeichert werden muss.</p>
<form action="{{ request.uri }}" method=post enctype=application/x-www-form-urlencoded>
<fieldset>
<legend>Neues API-Passwort setzen oder API-Passwort-Zugang deaktivieren</legend>
<dl>
<dt>Aktuell</dt>
<dd>{% raw "es ist ein API-Passwort gesetzt" if use_api_password else "es ist <em>kein</em> API-Passwort gesetzt" %}</dd>
<dt><label for=password>Neues Passwort</label></dt>
<dd>
<input type=password name=password id=password value="" />
<p class=info>Das API-Passwort wird als gesalzter SHA1-Hash
abgelegt, daher sind hierfür 40 Zeichen empfohlen.</p>
<script type=text/javascript>
document.write('<p title="Das neue API-Passwort im Klartext anzeigen">' +
'<label><input type=checkbox id=passwd_toggle' +
' onchange="javascript:toggle_password_visibility()" />' +
' Passwort anzeigen</label></p>')
var passwd = document.getElementById('password');
function toggle_password_visibility() {
if (passwd.getAttribute('type') == 'password') {
passwd.setAttribute('type', 'text');
} else {
passwd.setAttribute('type', 'password');
}
}
var passwd_toggle = document.getElementById('passwd_toggle');
// When having NoScript active, disabling it and checking `Plain password',
// then turning NoScript on and off again, the checkbox `Plain password'
// would be still checked but the password shown masked by stars. To get a
// correct behaviour we have to test if a change of the password visibility
// would be necessary or not. We simply could just uncheck the checkbox
// hardly but if the Browser keeps state of the controls, we'll do our best
// to cope with this issue — maybe hiding the password always by default
// would be safer in terms of security.
// Yeah, kind of strange… …but this is web developing in still 2012. And on
// a side note see this huge block of comment for just two lines of actual
// source code.
if (!(passwd_toggle.checked ^ passwd.getAttribute('type') == 'password')) {
toggle_password_visibility();
}
</script>
</dd>
<dt><label for=deactivate>Löschen</label></dt>
<dd><label title="Zugang über das API-Passwort deaktivieren">
<input type=checkbox name=deactivate id=deactivate value=1 />
API-Passwort löschen (API nur über das IsoBeef-<br />Benutzer-Passwort
zugänglich machen)</label></dd>
</dl>
<p class=buttonbox>
<input type=submit name=change value=Ändern title="API-Passwort ändern"/>
<input type=submit name=remove value=Löschen title="API-Passwort löschen"/>
</p>
</fieldset>
</form>
</section>
{% end %}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment