1.1 --- a/docs/wiki/Changelog Sat Apr 23 00:41:58 2016 +0200
1.2 +++ b/docs/wiki/Changelog Fri May 06 23:06:35 2016 +0200
1.3 @@ -1,5 +1,9 @@
1.4 = Changelog =
1.5
1.6 +== New in imip-agent 0.3 (Changes since imip-agent 0.2) ==
1.7 +
1.8 + * Added validation tokens to forms in the management interface.
1.9 +
1.10 == New in imip-agent 0.2 (Changes since imip-agent 0.1) ==
1.11
1.12 * Added support for [[../DatabaseStore|PostgreSQL databases]] to hold stored
2.1 --- a/imipweb/calendar.py Sat Apr 23 00:41:58 2016 +0200
2.2 +++ b/imipweb/calendar.py Fri May 06 23:06:35 2016 +0200
2.3 @@ -45,6 +45,11 @@
2.4
2.5 _ = self.get_translator()
2.6
2.7 + # Check the validation token.
2.8 +
2.9 + if not self.check_validation_token():
2.10 + return False
2.11 +
2.12 # Handle a submitted form.
2.13
2.14 args = self.env.get_args()
2.15 @@ -597,7 +602,7 @@
2.16 # Form controls are used in various places on the calendar page.
2.17
2.18 page.form(method="POST")
2.19 -
2.20 + self.validator()
2.21 self.show_user_navigation()
2.22 self.show_requests_on_page()
2.23 self.show_participants_on_page(participants)
3.1 --- a/imipweb/event.py Sat Apr 23 00:41:58 2016 +0200
3.2 +++ b/imipweb/event.py Fri May 06 23:06:35 2016 +0200
3.3 @@ -198,6 +198,7 @@
3.4
3.5 page = self.page
3.6 page.form(method="POST")
3.7 + self.validator()
3.8
3.9 # Add a hidden control to help determine whether editing has already begun.
3.10
3.11 @@ -821,6 +822,11 @@
3.12 if not have_action:
3.13 return ["action"]
3.14
3.15 + # Check the validation token.
3.16 +
3.17 + if not self.check_validation_token():
3.18 + return ["token"]
3.19 +
3.20 # If ignoring the object, return to the calendar.
3.21
3.22 if ignore:
4.1 --- a/imipweb/profile.py Sat Apr 23 00:41:58 2016 +0200
4.2 +++ b/imipweb/profile.py Fri May 06 23:06:35 2016 +0200
4.3 @@ -58,6 +58,11 @@
4.4 if not action:
4.5 return ["action"]
4.6
4.7 + # Check the validation token.
4.8 +
4.9 + if not self.check_validation_token():
4.10 + return ["token"]
4.11 +
4.12 if save:
4.13 errors = self.update_preferences()
4.14 if errors:
4.15 @@ -199,6 +204,7 @@
4.16
4.17 self.new_page(title=_("Profile"))
4.18 page.form(method="POST")
4.19 + self.validator()
4.20 self.show_preferences(errors)
4.21 self.show_controls()
4.22 page.form.close()
5.1 --- a/imipweb/resource.py Sat Apr 23 00:41:58 2016 +0200
5.2 +++ b/imipweb/resource.py Fri May 06 23:06:35 2016 +0200
5.3 @@ -29,8 +29,10 @@
5.4 from imipweb.env import CGIEnvironment
5.5 from urllib import urlencode
5.6 import babel.dates
5.7 +import hashlib, hmac
5.8 import markup
5.9 import pytz
5.10 +import time
5.11
5.12 class Resource:
5.13
5.14 @@ -224,6 +226,42 @@
5.15
5.16 "Utility methods resource mix-in."
5.17
5.18 + def get_validation_token(self, details=None):
5.19 +
5.20 + "Return a token suitable for validating a form submission."
5.21 +
5.22 + # Use a secret held in the user's preferences.
5.23 +
5.24 + prefs = self.get_preferences()
5.25 + if not prefs.has_key("secret"):
5.26 + prefs["secret"] = str(time.time())
5.27 +
5.28 + # Combine it with the user identity and any supplied details.
5.29 +
5.30 + secret = prefs["secret"].encode("utf-8")
5.31 + details = u"".join([self.env.get_user()] + (details or [])).encode("utf-8")
5.32 +
5.33 + return hmac.new(secret, details, hashlib.sha256).hexdigest()
5.34 +
5.35 + def check_validation_token(self, name="token", details=None):
5.36 +
5.37 + """
5.38 + Check the field having the given 'name', returning if its value matches
5.39 + the validation token generated using any given 'details'.
5.40 + """
5.41 +
5.42 + return self.env.get_args().get(name, [None])[0] == self.get_validation_token(details)
5.43 +
5.44 + def validator(self, name="token", details=None):
5.45 +
5.46 + """
5.47 + Show a control having the given 'name' that is used to validate form
5.48 + submissions, employing any additional 'details' in the construction of
5.49 + the validation token.
5.50 + """
5.51 +
5.52 + self.page.input(name=name, type="hidden", value=self.get_validation_token(details))
5.53 +
5.54 def prefixed_args(self, prefix, convert=None):
5.55
5.56 """