Add common "form poster" logic, to make CSRF token/header names configurable
also refactor the Feedback logic to use it
This commit is contained in:
parent
a801672821
commit
cc833c52b6
|
@ -43,7 +43,7 @@ from zope.sqlalchemy import register
|
|||
|
||||
import tailbone.db
|
||||
from tailbone.auth import TailboneAuthorizationPolicy
|
||||
|
||||
from tailbone.config import csrf_token_name, csrf_header_name
|
||||
from tailbone.util import get_effective_theme, get_theme_template_path
|
||||
|
||||
|
||||
|
@ -130,9 +130,12 @@ def make_pyramid_config(settings, configure_csrf=True):
|
|||
config.set_authorization_policy(TailboneAuthorizationPolicy())
|
||||
config.set_authentication_policy(SessionAuthenticationPolicy())
|
||||
|
||||
# always require CSRF token protection
|
||||
# maybe require CSRF token protection
|
||||
if configure_csrf:
|
||||
config.set_default_csrf_options(require_csrf=True, token='_csrf')
|
||||
rattail_config = settings['rattail_config']
|
||||
config.set_default_csrf_options(require_csrf=True,
|
||||
token=csrf_token_name(rattail_config),
|
||||
header=csrf_header_name(rattail_config))
|
||||
|
||||
# Bring in some Pyramid goodies.
|
||||
config.include('tailbone.beaker')
|
||||
|
|
|
@ -53,6 +53,14 @@ class ConfigExtension(BaseExtension):
|
|||
config.setdefault('tailbone', 'themes.expose_picker', 'true')
|
||||
|
||||
|
||||
def csrf_token_name(config):
|
||||
return config.get('tailbone', 'csrf_token_name', default='_csrf')
|
||||
|
||||
|
||||
def csrf_header_name(config):
|
||||
return config.get('tailbone', 'csrf_header_name', default='X-CSRF-TOKEN')
|
||||
|
||||
|
||||
def global_help_url(config):
|
||||
return config.get('tailbone', 'global_help_url')
|
||||
|
||||
|
@ -64,4 +72,3 @@ def legacy_mobile_enabled(config):
|
|||
|
||||
def protected_usernames(config):
|
||||
return config.getlist('tailbone', 'protected_usernames')
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
let FeedbackForm = {
|
||||
props: ['action', 'message'],
|
||||
template: '#feedback-template',
|
||||
mixins: [FormPosterMixin],
|
||||
methods: {
|
||||
|
||||
showFeedback() {
|
||||
|
@ -20,30 +21,11 @@ let FeedbackForm = {
|
|||
message: this.message.trim(),
|
||||
}
|
||||
|
||||
let headers = {
|
||||
// TODO: should find a better way to handle CSRF token
|
||||
'X-CSRF-TOKEN': this.csrftoken,
|
||||
}
|
||||
|
||||
this.$http.post(this.action, params, {headers: headers}).then(({ data }) => {
|
||||
if (data.ok) {
|
||||
this.submitForm(this.action, params, response => {
|
||||
alert("Message successfully sent.\n\nThank you for your feedback.")
|
||||
this.showDialog = false
|
||||
// clear out message, in case they need to send another
|
||||
this.message = ""
|
||||
} else {
|
||||
this.$buefy.toast.open({
|
||||
message: "Failed to send feedback: " + data.error,
|
||||
type: 'is-danger',
|
||||
duration: 4000, // 4 seconds
|
||||
})
|
||||
}
|
||||
}, response => {
|
||||
this.$buefy.toast.open({
|
||||
message: "Failed to send feedback! (unknown server error)",
|
||||
type: 'is-danger',
|
||||
duration: 4000, // 4 seconds
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ from webhelpers2.html import tags
|
|||
import tailbone
|
||||
from tailbone import helpers
|
||||
from tailbone.db import Session
|
||||
from tailbone.config import csrf_header_name
|
||||
from tailbone.menus import make_simple_menus
|
||||
|
||||
|
||||
|
@ -106,6 +107,7 @@ def before_render(event):
|
|||
renderer_globals['datetime'] = datetime
|
||||
renderer_globals['colander'] = colander
|
||||
renderer_globals['deform'] = deform
|
||||
renderer_globals['csrf_header_name'] = csrf_header_name(request.rattail_config)
|
||||
|
||||
# theme - we only want do this for classic web app, *not* API
|
||||
# TODO: so, clearly we need a better way to distinguish the two
|
||||
|
|
42
tailbone/templates/formposter.mako
Normal file
42
tailbone/templates/formposter.mako
Normal file
|
@ -0,0 +1,42 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
|
||||
<%def name="declare_formposter_mixin()">
|
||||
<script type="text/javascript">
|
||||
|
||||
let FormPosterMixin = {
|
||||
methods: {
|
||||
|
||||
submitForm(action, params, success) {
|
||||
|
||||
let csrftoken = ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n}
|
||||
|
||||
let headers = {
|
||||
'${csrf_header_name}': csrftoken,
|
||||
}
|
||||
|
||||
this.$http.post(action, params, {headers: headers}).then(response => {
|
||||
|
||||
if (response.data.ok) {
|
||||
success(response)
|
||||
|
||||
} else {
|
||||
this.$buefy.toast.open({
|
||||
message: "Failed to send feedback: " + response.data.error,
|
||||
type: 'is-danger',
|
||||
duration: 4000, // 4 seconds
|
||||
})
|
||||
}
|
||||
|
||||
}, response => {
|
||||
this.$buefy.toast.open({
|
||||
message: "Failed to submit form! (unknown server error)",
|
||||
type: 'is-danger',
|
||||
duration: 4000, // 4 seconds
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
|
@ -2,6 +2,7 @@
|
|||
<%namespace file="/grids/nav.mako" import="grid_index_nav" />
|
||||
<%namespace file="/autocomplete.mako" import="tailbone_autocomplete_template" />
|
||||
<%namespace name="base_meta" file="/base_meta.mako" />
|
||||
<%namespace file="/formposter.mako" import="declare_formposter_mixin" />
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
@ -487,6 +488,7 @@
|
|||
</%def>
|
||||
|
||||
<%def name="declare_whole_page_vars()">
|
||||
${declare_formposter_mixin()}
|
||||
${h.javascript_link(request.static_url('tailbone:static/themes/falafel/js/tailbone.feedback.js') + '?ver={}'.format(tailbone.__version__))}
|
||||
<script type="text/javascript">
|
||||
|
||||
|
@ -523,7 +525,6 @@
|
|||
<%def name="modify_whole_page_vars()">
|
||||
<script type="text/javascript">
|
||||
|
||||
FeedbackFormData.csrftoken = ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n}
|
||||
FeedbackFormData.referrer = location.href
|
||||
|
||||
% if request.user:
|
||||
|
|
Loading…
Reference in a new issue