diff --git a/www/kojiweb/buildtargetedit.chtml b/www/kojiweb/buildtargetedit.chtml index f550011c..647a87c5 100644 --- a/www/kojiweb/buildtargetedit.chtml +++ b/www/kojiweb/buildtargetedit.chtml @@ -9,6 +9,7 @@ #end if
+ $util.authToken($self, form=True) #if $target #end if diff --git a/www/kojiweb/buildtargetinfo.chtml b/www/kojiweb/buildtargetinfo.chtml index 97f5ff32..bdc33248 100644 --- a/www/kojiweb/buildtargetinfo.chtml +++ b/www/kojiweb/buildtargetinfo.chtml @@ -19,10 +19,10 @@ #if 'admin' in $perms - Edit + Edit - Delete + Delete #end if diff --git a/www/kojiweb/buildtargets.chtml b/www/kojiweb/buildtargets.chtml index 395ca413..ad02cbd9 100644 --- a/www/kojiweb/buildtargets.chtml +++ b/www/kojiweb/buildtargets.chtml @@ -70,7 +70,7 @@ #if 'admin' in $perms
- Create new Build Target + Create new Build Target #end if #include "includes/footer.chtml" diff --git a/www/kojiweb/hostinfo.chtml b/www/kojiweb/hostinfo.chtml index 42536363..0455aa3e 100644 --- a/www/kojiweb/hostinfo.chtml +++ b/www/kojiweb/hostinfo.chtml @@ -27,9 +27,9 @@ $util.imageTag($enabled) #if 'admin' in $perms #if $host.enabled - (disable) + (disable) #else - (enable) + (enable) #end if #end if diff --git a/www/kojiweb/index.chtml b/www/kojiweb/index.chtml index 139ef012..b612ffcc 100644 --- a/www/kojiweb/index.chtml +++ b/www/kojiweb/index.chtml @@ -168,8 +168,8 @@ #if $notif.package then $notif.package.name else 'all'# #if $notif.tag then $notif.tag.name else 'all'# #if $notif.success_only then 'success only' else 'all'# - edit - delete + edit + delete #end for #if $len($notifs) == 0 @@ -180,7 +180,7 @@
- Add a Notification + Add a Notification #end if #include "includes/footer.chtml" diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 1047c0b6..ad8d684e 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -8,6 +8,7 @@ import Cheetah.Filters import Cheetah.Template import datetime import time +import md5 import koji import kojiweb.util @@ -49,6 +50,30 @@ def _getUserCookie(req): return None +def _truncTime(): + now = datetime.datetime.now() + # truncate to the nearest 15 minutes + return now.replace(minute=(now.minute / 15 * 15), second=0, microsecond=0) + +def _genToken(req, tstamp=None): + if hasattr(req, 'currentLogin') and req.currentLogin: + user = req.currentLogin + else: + return '' + if tstamp == None: + tstamp = _truncTime() + return md5.new(user + str(tstamp) + req.get_options()['Secret']).hexdigest()[-8:] + +def _getValidTokens(req): + tokens = [] + now = _truncTime() + for delta in (0, 15, 30): + token_time = now - datetime.timedelta(minutes=delta) + token = _genToken(req, token_time) + if token: + tokens.append(token) + return tokens + def _krbLogin(req, session, principal): options = req.get_options() wprinc = options['WebPrincipal'] @@ -81,6 +106,19 @@ def _assertLogin(req): raise koji.AuthError, 'could not login using principal: %s' % req.currentLogin else: raise koji.AuthError, 'KojiWeb is incorrectly configured for authentication, contact the system administrator' + + # verify a valid authToken was passed in to avoid CSRF + authToken = req.form.get('a', '') + validTokens = _getValidTokens(req) + if authToken and authToken in validTokens: + # we have a token and it's valid + pass + else: + # their authToken is likely expired + # send them back to the page that brought them here so they + # can re-click the link with a valid authToken + _redirectBack(req, page=None, forceSSL=(_getBaseURL(req).startswith('https://'))) + assert False else: mod_python.util.redirect(req, 'login') assert False @@ -117,7 +155,8 @@ def _genHTML(req, fileName): req._values['currentUser'] = req.currentUser else: req._values['currentUser'] = None - + req._values['authToken'] = _genToken(req) + tmpl_class = TEMPLATES.get(fileName) if not tmpl_class: tmpl_class = Cheetah.Template.Template.compile(file=fileName) diff --git a/www/kojiweb/notificationedit.chtml b/www/kojiweb/notificationedit.chtml index 1f2f5488..12aaf3dc 100644 --- a/www/kojiweb/notificationedit.chtml +++ b/www/kojiweb/notificationedit.chtml @@ -9,6 +9,7 @@ #end if + $util.authToken($self, form=True) #if $notif #end if diff --git a/www/kojiweb/tagedit.chtml b/www/kojiweb/tagedit.chtml index 8257c42f..a6b60e21 100644 --- a/www/kojiweb/tagedit.chtml +++ b/www/kojiweb/tagedit.chtml @@ -9,6 +9,7 @@ #end if + $util.authToken($self, form=True) diff --git a/www/kojiweb/taginfo.chtml b/www/kojiweb/taginfo.chtml index b1e1d354..be839a78 100644 --- a/www/kojiweb/taginfo.chtml +++ b/www/kojiweb/taginfo.chtml @@ -7,7 +7,7 @@
Name
#if $child and 'admin' in $perms - + #end if @@ -54,7 +54,7 @@ $parent.name #if $depth == 1 and 'admin' in $perms - (edit) (remove) + (edit) (remove) #end if @@ -125,10 +125,10 @@ #if 'admin' in $perms - + - + #end if
Add $tag.name as parent of $child.nameAdd $tag.name as parent of $child.name
Edit tagEdit tag
Delete tagDelete tag
diff --git a/www/kojiweb/tagparent.chtml b/www/kojiweb/tagparent.chtml index 4cb9052c..cd391f22 100644 --- a/www/kojiweb/tagparent.chtml +++ b/www/kojiweb/tagparent.chtml @@ -9,6 +9,7 @@ #end if + $util.authToken($self, form=True) diff --git a/www/kojiweb/tags.chtml b/www/kojiweb/tags.chtml index 39658d64..fe39118b 100644 --- a/www/kojiweb/tags.chtml +++ b/www/kojiweb/tags.chtml @@ -70,7 +70,7 @@ #if 'admin' in $perms
- Create new Tag + Create new Tag #end if #include "includes/footer.chtml" diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml index 16b168b2..fe2b6147 100644 --- a/www/kojiweb/taskinfo.chtml +++ b/www/kojiweb/taskinfo.chtml @@ -171,9 +171,9 @@ diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index 46c189ec..212fb070 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -316,6 +316,23 @@ def escapeHTML(value): replace('<', '<').\ replace('>', '>') +def authToken(template, first=False, form=False): + """Return the current authToken if it exists. + If form is True, return it enclosed in a hidden input field. + Otherwise, return it in a format suitable for appending to a URL. + If first is True, prefix it with ?, otherwise prefix it + with &. If no authToken exists, return an empty string.""" + token = template.getVar('authToken', default=None) + if token != None: + if form: + return '' % token + if first: + return '?a=' + token + else: + return '&a=' + token + else: + return '' + def explainError(error): """Explain an exception in user-consumable terms
$state #if $currentUser and ('admin' in $perms or $task.owner == $currentUser.id) #if $task.state in ($koji.TASK_STATES.FREE, $koji.TASK_STATES.OPEN, $koji.TASK_STATES.ASSIGNED) - (cancel) + (cancel) #elif $task.state in ($koji.TASK_STATES.CANCELED, $koji.TASK_STATES.FAILED) and (not $parent) - (resubmit) + (resubmit) #end if #end if