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
|
import tailbone.db
|
||||||
from tailbone.auth import TailboneAuthorizationPolicy
|
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
|
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_authorization_policy(TailboneAuthorizationPolicy())
|
||||||
config.set_authentication_policy(SessionAuthenticationPolicy())
|
config.set_authentication_policy(SessionAuthenticationPolicy())
|
||||||
|
|
||||||
# always require CSRF token protection
|
# maybe require CSRF token protection
|
||||||
if configure_csrf:
|
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.
|
# Bring in some Pyramid goodies.
|
||||||
config.include('tailbone.beaker')
|
config.include('tailbone.beaker')
|
||||||
|
|
|
@ -53,6 +53,14 @@ class ConfigExtension(BaseExtension):
|
||||||
config.setdefault('tailbone', 'themes.expose_picker', 'true')
|
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):
|
def global_help_url(config):
|
||||||
return config.get('tailbone', 'global_help_url')
|
return config.get('tailbone', 'global_help_url')
|
||||||
|
|
||||||
|
@ -64,4 +72,3 @@ def legacy_mobile_enabled(config):
|
||||||
|
|
||||||
def protected_usernames(config):
|
def protected_usernames(config):
|
||||||
return config.getlist('tailbone', 'protected_usernames')
|
return config.getlist('tailbone', 'protected_usernames')
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
let FeedbackForm = {
|
let FeedbackForm = {
|
||||||
props: ['action', 'message'],
|
props: ['action', 'message'],
|
||||||
template: '#feedback-template',
|
template: '#feedback-template',
|
||||||
|
mixins: [FormPosterMixin],
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
showFeedback() {
|
showFeedback() {
|
||||||
|
@ -20,30 +21,11 @@ let FeedbackForm = {
|
||||||
message: this.message.trim(),
|
message: this.message.trim(),
|
||||||
}
|
}
|
||||||
|
|
||||||
let headers = {
|
this.submitForm(this.action, params, response => {
|
||||||
// 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) {
|
|
||||||
alert("Message successfully sent.\n\nThank you for your feedback.")
|
alert("Message successfully sent.\n\nThank you for your feedback.")
|
||||||
this.showDialog = false
|
this.showDialog = false
|
||||||
// clear out message, in case they need to send another
|
// clear out message, in case they need to send another
|
||||||
this.message = ""
|
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
|
import tailbone
|
||||||
from tailbone import helpers
|
from tailbone import helpers
|
||||||
from tailbone.db import Session
|
from tailbone.db import Session
|
||||||
|
from tailbone.config import csrf_header_name
|
||||||
from tailbone.menus import make_simple_menus
|
from tailbone.menus import make_simple_menus
|
||||||
|
|
||||||
|
|
||||||
|
@ -106,6 +107,7 @@ def before_render(event):
|
||||||
renderer_globals['datetime'] = datetime
|
renderer_globals['datetime'] = datetime
|
||||||
renderer_globals['colander'] = colander
|
renderer_globals['colander'] = colander
|
||||||
renderer_globals['deform'] = deform
|
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
|
# theme - we only want do this for classic web app, *not* API
|
||||||
# TODO: so, clearly we need a better way to distinguish the two
|
# 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="/grids/nav.mako" import="grid_index_nav" />
|
||||||
<%namespace file="/autocomplete.mako" import="tailbone_autocomplete_template" />
|
<%namespace file="/autocomplete.mako" import="tailbone_autocomplete_template" />
|
||||||
<%namespace name="base_meta" file="/base_meta.mako" />
|
<%namespace name="base_meta" file="/base_meta.mako" />
|
||||||
|
<%namespace file="/formposter.mako" import="declare_formposter_mixin" />
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
@ -487,6 +488,7 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="declare_whole_page_vars()">
|
<%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__))}
|
${h.javascript_link(request.static_url('tailbone:static/themes/falafel/js/tailbone.feedback.js') + '?ver={}'.format(tailbone.__version__))}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
@ -523,7 +525,6 @@
|
||||||
<%def name="modify_whole_page_vars()">
|
<%def name="modify_whole_page_vars()">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
FeedbackFormData.csrftoken = ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n}
|
|
||||||
FeedbackFormData.referrer = location.href
|
FeedbackFormData.referrer = location.href
|
||||||
|
|
||||||
% if request.user:
|
% if request.user:
|
||||||
|
|
Loading…
Reference in a new issue