# HG changeset patch # User Paul Boddie # Date 1462568795 -7200 # Node ID 88bd64e4b07aae7599e9e05840221c3755aee833 # Parent 432b0f87a94b2af7a681c17f3de1133565303feb Added validation tokens to forms in the management interface. diff -r 432b0f87a94b -r 88bd64e4b07a docs/wiki/Changelog --- a/docs/wiki/Changelog Sat Apr 23 00:41:58 2016 +0200 +++ b/docs/wiki/Changelog Fri May 06 23:06:35 2016 +0200 @@ -1,5 +1,9 @@ = Changelog = +== New in imip-agent 0.3 (Changes since imip-agent 0.2) == + + * Added validation tokens to forms in the management interface. + == New in imip-agent 0.2 (Changes since imip-agent 0.1) == * Added support for [[../DatabaseStore|PostgreSQL databases]] to hold stored diff -r 432b0f87a94b -r 88bd64e4b07a imipweb/calendar.py --- a/imipweb/calendar.py Sat Apr 23 00:41:58 2016 +0200 +++ b/imipweb/calendar.py Fri May 06 23:06:35 2016 +0200 @@ -45,6 +45,11 @@ _ = self.get_translator() + # Check the validation token. + + if not self.check_validation_token(): + return False + # Handle a submitted form. args = self.env.get_args() @@ -597,7 +602,7 @@ # Form controls are used in various places on the calendar page. page.form(method="POST") - + self.validator() self.show_user_navigation() self.show_requests_on_page() self.show_participants_on_page(participants) diff -r 432b0f87a94b -r 88bd64e4b07a imipweb/event.py --- a/imipweb/event.py Sat Apr 23 00:41:58 2016 +0200 +++ b/imipweb/event.py Fri May 06 23:06:35 2016 +0200 @@ -198,6 +198,7 @@ page = self.page page.form(method="POST") + self.validator() # Add a hidden control to help determine whether editing has already begun. @@ -821,6 +822,11 @@ if not have_action: return ["action"] + # Check the validation token. + + if not self.check_validation_token(): + return ["token"] + # If ignoring the object, return to the calendar. if ignore: diff -r 432b0f87a94b -r 88bd64e4b07a imipweb/profile.py --- a/imipweb/profile.py Sat Apr 23 00:41:58 2016 +0200 +++ b/imipweb/profile.py Fri May 06 23:06:35 2016 +0200 @@ -58,6 +58,11 @@ if not action: return ["action"] + # Check the validation token. + + if not self.check_validation_token(): + return ["token"] + if save: errors = self.update_preferences() if errors: @@ -199,6 +204,7 @@ self.new_page(title=_("Profile")) page.form(method="POST") + self.validator() self.show_preferences(errors) self.show_controls() page.form.close() diff -r 432b0f87a94b -r 88bd64e4b07a imipweb/resource.py --- a/imipweb/resource.py Sat Apr 23 00:41:58 2016 +0200 +++ b/imipweb/resource.py Fri May 06 23:06:35 2016 +0200 @@ -29,8 +29,10 @@ from imipweb.env import CGIEnvironment from urllib import urlencode import babel.dates +import hashlib, hmac import markup import pytz +import time class Resource: @@ -224,6 +226,42 @@ "Utility methods resource mix-in." + def get_validation_token(self, details=None): + + "Return a token suitable for validating a form submission." + + # Use a secret held in the user's preferences. + + prefs = self.get_preferences() + if not prefs.has_key("secret"): + prefs["secret"] = str(time.time()) + + # Combine it with the user identity and any supplied details. + + secret = prefs["secret"].encode("utf-8") + details = u"".join([self.env.get_user()] + (details or [])).encode("utf-8") + + return hmac.new(secret, details, hashlib.sha256).hexdigest() + + def check_validation_token(self, name="token", details=None): + + """ + Check the field having the given 'name', returning if its value matches + the validation token generated using any given 'details'. + """ + + return self.env.get_args().get(name, [None])[0] == self.get_validation_token(details) + + def validator(self, name="token", details=None): + + """ + Show a control having the given 'name' that is used to validate form + submissions, employing any additional 'details' in the construction of + the validation token. + """ + + self.page.input(name=name, type="hidden", value=self.get_validation_token(details)) + def prefixed_args(self, prefix, convert=None): """