Adding Flask-SeaSurf module for CSRF protection.

This commit is contained in:
Khanh Ngo 2018-11-21 10:24:33 +07:00
parent ea53ae340f
commit 5f049debe5
25 changed files with 93 additions and 77 deletions

View file

@ -5,6 +5,7 @@ from flask_sqlalchemy import SQLAlchemy as SA
from flask_migrate import Migrate from flask_migrate import Migrate
from authlib.flask.client import OAuth as AuthlibOAuth from authlib.flask.client import OAuth as AuthlibOAuth
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
from flask_seasurf import SeaSurf
# subclass SQLAlchemy to enable pool_pre_ping # subclass SQLAlchemy to enable pool_pre_ping
class SQLAlchemy(SA): class SQLAlchemy(SA):
@ -18,6 +19,7 @@ from app.assets import assets
app = Flask(__name__) app = Flask(__name__)
app.config.from_object('config') app.config.from_object('config')
app.wsgi_app = ProxyFix(app.wsgi_app) app.wsgi_app = ProxyFix(app.wsgi_app)
csrf = SeaSurf(app)
assets.init_app(app) assets.init_app(app)

View file

@ -145,8 +145,8 @@ function SelectElement(elementID, valueToSelect)
element.value = valueToSelect; element.value = valueToSelect;
} }
function enable_dns_sec(url) { function enable_dns_sec(url, csrf_token) {
$.getJSON(url, function(data) { $.post(url, {'_csrf_token': csrf_token}, function(data) {
var modal = $("#modal_dnssec_info"); var modal = $("#modal_dnssec_info");
if (data['status'] == 'error'){ if (data['status'] == 'error'){
@ -157,7 +157,7 @@ function enable_dns_sec(url) {
//location.reload(); //location.reload();
window.location.reload(true); window.location.reload(true);
} }
}) }, 'json')
} }
function getdnssec(url, domain){ function getdnssec(url, domain){

View file

@ -28,6 +28,7 @@
<!-- /.box-header --> <!-- /.box-header -->
<!-- form start --> <!-- form start -->
<form role="form" method="post" action="{% if create %}{{ url_for('admin_editaccount') }}{% else %}{{ url_for('admin_editaccount', account_name=account.name) }}{% endif %}"> <form role="form" method="post" action="{% if create %}{{ url_for('admin_editaccount') }}{% else %}{{ url_for('admin_editaccount', account_name=account.name) }}{% endif %}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="create" value="{{ create }}"> <input type="hidden" name="create" value="{{ create }}">
<div class="box-body"> <div class="box-body">
{% if error %} {% if error %}
@ -116,4 +117,4 @@
<script> <script>
$("#account_multi_user").multiSelect(); $("#account_multi_user").multiSelect();
</script> </script>
{% endblock %} {% endblock %}

View file

@ -28,6 +28,7 @@
<!-- /.box-header --> <!-- /.box-header -->
<!-- form start --> <!-- form start -->
<form role="form" method="post" action="{% if create %}{{ url_for('admin_edituser') }}{% else %}{{ url_for('admin_edituser', user_username=user.username) }}{% endif %}"> <form role="form" method="post" action="{% if create %}{{ url_for('admin_edituser') }}{% else %}{{ url_for('admin_edituser', user_username=user.username) }}{% endif %}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="create" value="{{ create }}"> <input type="hidden" name="create" value="{{ create }}">
<div class="box-body"> <div class="box-body">
{% if error %} {% if error %}
@ -115,14 +116,14 @@
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// handle disabling two factor authentication // handle disabling two factor authentication
$(document.body).on('click', '.button_otp_disable', function() { $(document.body).on('click', '.button_otp_disable', function() {
var modal = $("#modal_otp_disable"); var modal = $("#modal_otp_disable");
var username = $(this).prop('id'); var username = $(this).prop('id');
var info = "Are you sure you want to disable two factor authentication for user " + username + "?"; var info = "Are you sure you want to disable two factor authentication for user " + username + "?";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_otp_disable_confirm').click(function() { modal.find('#button_otp_disable_confirm').click(function() {
var postdata = {'action': 'user_otp_disable', 'data': username} var postdata = {'action': 'user_otp_disable', 'data': username, '_csrf_token': '{{ csrf_token() }}'}
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser', false, true); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser', false, true);
}) })
modal.modal('show'); modal.modal('show');

View file

@ -60,7 +60,7 @@
</div> </div>
<!-- /.row --> <!-- /.row -->
</section> </section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// set up history data table // set up history data table
@ -89,7 +89,7 @@
modal.modal('show'); modal.modal('show');
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<!-- Clear History Confirmation Box --> <!-- Clear History Confirmation Box -->
<div class="modal fade modal-warning" id="modal_clear_history"> <div class="modal fade modal-warning" id="modal_clear_history">
@ -108,7 +108,7 @@
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-flat btn-default pull-left" <button type="button" class="btn btn-flat btn-default pull-left"
data-dismiss="modal">Close</button> data-dismiss="modal">Close</button>
<button type="button" class="btn btn-flat btn-danger" onclick="applyChanges('', $SCRIPT_ROOT + '/admin/history', false, true);">Clear History</button> <button type="button" class="btn btn-flat btn-danger" onclick="applyChanges({'_csrf_token': '{{ csrf_token() }}'}, $SCRIPT_ROOT + '/admin/history', false, true);">Clear History</button>
</div> </div>
</div> </div>
<!-- /.modal-content --> <!-- /.modal-content -->

View file

@ -67,7 +67,7 @@
</div> </div>
<!-- /.row --> <!-- /.row -->
</section> </section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// set up accounts data table // set up accounts data table
@ -90,16 +90,16 @@
$(document.body).on('click', '.button_delete', function() { $(document.body).on('click', '.button_delete', function() {
var modal = $("#modal_delete"); var modal = $("#modal_delete");
var accountname = $(this).prop('id'); var accountname = $(this).prop('id');
var info = "Are you sure you want to delete " + accountname + "?"; var info = "Are you sure you want to delete " + accountname + "?";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function() { modal.find('#button_delete_confirm').click(function() {
var postdata = {'action': 'delete_account', 'data': accountname} var postdata = {'action': 'delete_account', 'data': accountname, '_csrf_token': '{{ csrf_token() }}'}
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageaccount', false, true); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageaccount', false, true);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}

View file

@ -81,7 +81,7 @@
</div> </div>
<!-- /.row --> <!-- /.row -->
</section> </section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// set up user data table // set up user data table
@ -101,10 +101,10 @@
$(document.body).on('click', '.button_revoke', function() { $(document.body).on('click', '.button_revoke', function() {
var modal = $("#modal_revoke"); var modal = $("#modal_revoke");
var username = $(this).prop('id'); var username = $(this).prop('id');
var info = "Are you sure you want to revoke all privileges for " + username + ". They will not able to access any domain."; var info = "Are you sure you want to revoke all privileges for " + username + ". They will not able to access any domain.";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_revoke_confirm').click(function() { modal.find('#button_revoke_confirm').click(function() {
var postdata = {'action': 'revoke_user_privileges', 'data': username} var postdata = {'action': 'revoke_user_privileges', 'data': username, '_csrf_token': '{{ csrf_token() }}'}
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser'); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser');
modal.modal('hide'); modal.modal('hide');
}) })
@ -114,15 +114,15 @@
$(document.body).on('click', '.button_delete', function() { $(document.body).on('click', '.button_delete', function() {
var modal = $("#modal_delete"); var modal = $("#modal_delete");
var username = $(this).prop('id'); var username = $(this).prop('id');
var info = "Are you sure you want to delete " + username + "?"; var info = "Are you sure you want to delete " + username + "?";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function() { modal.find('#button_delete_confirm').click(function() {
var postdata = {'action': 'delete_user', 'data': username} var postdata = {'action': 'delete_user', 'data': username, '_csrf_token': '{{ csrf_token() }}'}
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser', false, true); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser', false, true);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
}); });
// handle user role changing // handle user role changing
@ -134,7 +134,8 @@
'data' : { 'data' : {
'username' : username, 'username' : username,
'role_name' : role_name 'role_name' : role_name
} },
'_csrf_token' : '{{ csrf_token() }}'
}; };
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser', showResult=true); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageuser', showResult=true);
}); });

View file

@ -57,6 +57,7 @@
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane active" id="tabs-general"> <div class="tab-pane active" id="tabs-general">
<form role="form" method="post"> <form role="form" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" value="general" name="config_tab" /> <input type="hidden" value="general" name="config_tab" />
<div class="form-group"> <div class="form-group">
<input type="checkbox" id="local_db_enabled" name="local_db_enabled" class="checkbox" {% if SETTING.get('local_db_enabled') %}checked{% endif %}> <input type="checkbox" id="local_db_enabled" name="local_db_enabled" class="checkbox" {% if SETTING.get('local_db_enabled') %}checked{% endif %}>
@ -75,6 +76,7 @@
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<form role="form" method="post" data-toggle="validator"> <form role="form" method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" value="ldap" name="config_tab" /> <input type="hidden" value="ldap" name="config_tab" />
<fieldset> <fieldset>
<legend>GENERAL</legend> <legend>GENERAL</legend>
@ -246,6 +248,7 @@
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<form role="form" method="post" data-toggle="validator"> <form role="form" method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" value="google" name="config_tab" /> <input type="hidden" value="google" name="config_tab" />
<fieldset> <fieldset>
<legend>GENERAL</legend> <legend>GENERAL</legend>
@ -303,6 +306,7 @@
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<form role="form" method="post" data-toggle="validator"> <form role="form" method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" value="github" name="config_tab" /> <input type="hidden" value="github" name="config_tab" />
<fieldset> <fieldset>
<legend>GENERAL</legend> <legend>GENERAL</legend>
@ -353,12 +357,13 @@
<legend>Help</legend> <legend>Help</legend>
<p>Fill in all the fields in the left form.</p> <p>Fill in all the fields in the left form.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="tab-pane" id="tabs-oidc"> <div class="tab-pane" id="tabs-oidc">
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<form role="form" method="post" data-toggle="validator"> <form role="form" method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" value="oidc" name="config_tab" /> <input type="hidden" value="oidc" name="config_tab" />
<fieldset> <fieldset>
<legend>GENERAL</legend> <legend>GENERAL</legend>

View file

@ -63,7 +63,7 @@
</div> </div>
<!-- /.row --> <!-- /.row -->
</section> </section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// set up history data table // set up history data table
@ -77,14 +77,14 @@
}); });
$(document.body).on('click', '.setting-toggle-button', function() { $(document.body).on('click', '.setting-toggle-button', function() {
var setting = $(this).prop('id'); var setting = $(this).prop('id');
applyChanges('', $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/toggle', false, true) applyChanges({'_csrf_token': '{{ csrf_token() }}'}, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/toggle', false, true)
}); });
$(document.body).on('click', '.setting-save-button', function() { $(document.body).on('click', '.setting-save-button', function() {
var setting = $(this).prop('id'); var setting = $(this).prop('id');
var value = $(this).parents('tr').find('#value')[0].value; var value = $(this).parents('tr').find('#value')[0].value;
var postdata = {'value': value}; var postdata = {'value': value, '_csrf_token': '{{ csrf_token() }}'};
applyChanges(postdata, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/edit', false, true) applyChanges(postdata, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/edit', false, true)
}); });
</script> </script>
{% endblock %} {% endblock %}

View file

@ -26,6 +26,7 @@
<!-- /.box-header --> <!-- /.box-header -->
<!-- form start --> <!-- form start -->
<form role="form" method="post" data-toggle="validator"> <form role="form" method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="box-body"> <div class="box-body">
{% if not SETTING.get('pdns_api_url') or not SETTING.get('pdns_api_key') or not SETTING.get('pdns_version') %} {% if not SETTING.get('pdns_api_url') or not SETTING.get('pdns_api_key') or not SETTING.get('pdns_version') %}
<div class="alert alert-danger alert-dismissible"> <div class="alert alert-danger alert-dismissible">
@ -82,4 +83,4 @@
{% assets "js_validation" -%} {% assets "js_validation" -%}
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{%- endassets %} {%- endassets %}
{% endblock %} {% endblock %}

View file

@ -26,6 +26,7 @@
<!-- /.box-header --> <!-- /.box-header -->
<!-- form start --> <!-- form start -->
<form role="form" method="post"> <form role="form" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="create" value="{{ create }}"> <input type="hidden" name="create" value="{{ create }}">
<div class="box-body"> <div class="box-body">
<table class="table table-bordered"> <table class="table table-bordered">
@ -75,4 +76,4 @@
increaseArea : '20%' increaseArea : '20%'
}) })
</script> </script>
{% endblock %} {% endblock %}

View file

@ -220,9 +220,9 @@
<input type=\"text\" class=\"form-control\" name=\"template_description\" id=\"template_description\" placeholder=\"Enter a template description (optional)\"> \ <input type=\"text\" class=\"form-control\" name=\"template_description\" id=\"template_description\" placeholder=\"Enter a template description (optional)\"> \
<input id=\"domain\" name=\"domain\" type=\"hidden\" value=\""+domain+"\"> \ <input id=\"domain\" name=\"domain\" type=\"hidden\" value=\""+domain+"\"> \
"; ";
modal.find('.modal-body p').html(form); modal.find('.modal-body p').html(form);
modal.find('#button_save').click(function() { modal.find('#button_save').click(function() {
var data = {}; var data = {'_csrf_token': '{{ csrf_token() }}'};
data['name'] = modal.find('#template_name').val(); data['name'] = modal.find('#template_name').val();
data['description'] = modal.find('#template_description').val(); data['description'] = modal.find('#template_description').val();
data['domain'] = modal.find('#domain').val(); data['domain'] = modal.find('#domain').val();
@ -232,7 +232,7 @@
modal.find('#button_close').click(function() { modal.find('#button_close').click(function() {
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
}); });
@ -244,13 +244,13 @@
$(document.body).on("click", ".button_dnssec_enable", function() { $(document.body).on("click", ".button_dnssec_enable", function() {
var domain = $(this).prop('id'); var domain = $(this).prop('id');
enable_dns_sec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec/enable'); enable_dns_sec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec/enable', '{{ csrf_token() }}');
}); });
$(document.body).on("click", ".button_dnssec_disable", function() { $(document.body).on("click", ".button_dnssec_disable", function() {
var domain = $(this).prop('id'); var domain = $(this).prop('id');
enable_dns_sec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec/disable'); enable_dns_sec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec/disable', '{{ csrf_token() }}');
}); });
{% endif %} {% endif %}

View file

@ -203,13 +203,13 @@
var modal = $("#modal_apply_changes"); var modal = $("#modal_apply_changes");
var table = $("#tbl_records").DataTable(); var table = $("#tbl_records").DataTable();
var domain = $(this).prop('id'); var domain = $(this).prop('id');
var serial = $(".button_apply_changes").val(); var serial = $(".button_apply_changes").val();
var info = "Are you sure you want to apply your changes?"; var info = "Are you sure you want to apply your changes?";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
// following unbind("click") is to avoid multiple times execution // following unbind("click") is to avoid multiple times execution
modal.find('#button_apply_confirm').unbind("click").click(function() { modal.find('#button_apply_confirm').unbind("click").click(function() {
var data = {'serial': serial, 'record': getTableData(table)}; var data = {'serial': serial, 'record': getTableData(table), '_csrf_token': '{{ csrf_token() }}'};
applyRecordChanges(data, domain); applyRecordChanges(data, domain);
modal.modal('hide'); modal.modal('hide');
}) })
@ -263,7 +263,7 @@
//handle update_from_master button //handle update_from_master button
$(document.body).on("click", ".button_update_from_master", function (e) { $(document.body).on("click", ".button_update_from_master", function (e) {
var domain = $(this).prop('id'); var domain = $(this).prop('id');
applyChanges({'domain': domain}, $SCRIPT_ROOT + '/domain/' + domain + '/update'); applyChanges({'domain': domain, '_csrf_token': '{{ csrf_token() }}'}, $SCRIPT_ROOT + '/domain/' + domain + '/update');
}); });
{% if SETTING.get('record_helper') %} {% if SETTING.get('record_helper') %}

View file

@ -28,6 +28,7 @@
<!-- /.box-header --> <!-- /.box-header -->
<!-- form start --> <!-- form start -->
<form role="form" method="post" action="{{ url_for('domain_add') }}"> <form role="form" method="post" action="{{ url_for('domain_add') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="box-body"> <div class="box-body">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" name="domain_name" id="domain_name" placeholder="Enter a valid domain name (required)"> <input type="text" class="form-control" name="domain_name" id="domain_name" placeholder="Enter a valid domain name (required)">

View file

@ -35,6 +35,7 @@
<div class="col-xs-12"> <div class="col-xs-12">
<div class="box"> <div class="box">
<form method="post" action="{{ url_for('domain_management', domain_name=domain.name) }}"> <form method="post" action="{{ url_for('domain_management', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="box-header"> <div class="box-header">
<h3 class="box-title">Domain Access Control</h3> <h3 class="box-title">Domain Access Control</h3>
</div> </div>
@ -81,6 +82,7 @@
<div class="col-xs-12"> <div class="col-xs-12">
<div class="form-group"> <div class="form-group">
<form method="post" action="{{ url_for('domain_change_account', domain_name=domain.name) }}"> <form method="post" action="{{ url_for('domain_change_account', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<select name="accountid" class="form-control" style="width:15em;"> <select name="accountid" class="form-control" style="width:15em;">
<option value="0">- No Account -</option> <option value="0">- No Account -</option>
{% for account in accounts %} {% for account in accounts %}
@ -108,7 +110,6 @@
{% for setting in domain.settings %}{% if setting.setting=='auto_ptr' and setting.value=='True' %}checked{% endif %}{% endfor %} {% if SETTING.get('auto_ptr') %}disabled="True"{% endif %}> {% for setting in domain.settings %}{% if setting.setting=='auto_ptr' and setting.value=='True' %}checked{% endif %}{% endfor %} {% if SETTING.get('auto_ptr') %}disabled="True"{% endif %}>
&nbsp;Allow automatic reverse pointer creation on record updates?{% if &nbsp;Allow automatic reverse pointer creation on record updates?{% if
SETTING.get('auto_ptr') %}</br><code>Auto-ptr is enabled globally on the PDA system!</code>{% endif %}</p> SETTING.get('auto_ptr') %}</br><code>Auto-ptr is enabled globally on the PDA system!</code>{% endif %}</p>
</div> </div>
</div> </div>
</div> </div>
@ -123,7 +124,6 @@
<p><input type="checkbox" id="{{ domain.name }}" class="dyndns_on_demand_toggle" <p><input type="checkbox" id="{{ domain.name }}" class="dyndns_on_demand_toggle"
{% for setting in domain.settings %}{% if setting.setting=='create_via_dyndns' and setting.value=='True' %}checked{% endif %}{% endfor %}> {% for setting in domain.settings %}{% if setting.setting=='create_via_dyndns' and setting.value=='True' %}checked{% endif %}{% endfor %}>
&nbsp;Allow on-demand creation of records via DynDNS updates?</p> &nbsp;Allow on-demand creation of records via DynDNS updates?</p>
</div> </div>
</div> </div>
</div> </div>
@ -152,6 +152,7 @@
</ul> </ul>
<b>New SOA-EDIT-API Setting:</b> <b>New SOA-EDIT-API Setting:</b>
<form method="post" action="{{ url_for('domain_change_soa_edit_api', domain_name=domain.name) }}"> <form method="post" action="{{ url_for('domain_change_soa_edit_api', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<select name="soa_edit_api" class="form-control" style="width:15em;"> <select name="soa_edit_api" class="form-control" style="width:15em;">
<option selected value="0">- Unchanged -</option> <option selected value="0">- Unchanged -</option>
<option>DEFAULT</option> <option>DEFAULT</option>
@ -207,7 +208,8 @@ $('.dyndns_on_demand_toggle').on('ifToggled', function(event) {
'data' : { 'data' : {
'setting' : 'create_via_dyndns', 'setting' : 'create_via_dyndns',
'value' : is_checked 'value' : is_checked
} },
'_csrf_token': '{{ csrf_token() }}'
}; };
applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/managesetting', true); applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/managesetting', true);
}); });
@ -219,7 +221,8 @@ $('.auto_ptr_toggle').on('ifToggled', function(event) {
'data' : { 'data' : {
'setting' : 'auto_ptr', 'setting' : 'auto_ptr',
'value' : is_checked 'value' : is_checked
} },
'_csrf_token': '{{ csrf_token() }}'
}; };
applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/managesetting', true); applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/managesetting', true);
}); });
@ -231,13 +234,13 @@ $(document.body).on('click', '.delete_domain', function() {
var info = "Are you sure you want to delete " + domain + "?"; var info = "Are you sure you want to delete " + domain + "?";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function() { modal.find('#button_delete_confirm').click(function() {
$.get($SCRIPT_ROOT + '/admin/domain/' + domain + '/delete', function() { $.post($SCRIPT_ROOT + '/admin/domain/' + domain + '/delete', { '_csrf_token': '{{ csrf_token() }}' }, function() {
window.location.href = '{{ url_for('dashboard') }}'; window.location.href = '{{ url_for('dashboard') }}';
}); });
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
}); });
</script> </script>
{% endblock %} {% endblock %}

View file

@ -32,6 +32,7 @@
</div> </div>
{% endif %} {% endif %}
<form action="" method="post" data-toggle="validator"> <form action="" method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" placeholder="Username" name="username" data-error="Please input your username" required {% if username %}value="{{ username }}"{% endif %}> <input type="text" class="form-control" placeholder="Username" name="username" data-error="Please input your username" required {% if username %}value="{{ username }}"{% endif %}>
<span class="help-block with-errors"></span> <span class="help-block with-errors"></span>

View file

@ -32,6 +32,7 @@
{% endif %} {% endif %}
<p class="login-box-msg">Enter your personal details below</p> <p class="login-box-msg">Enter your personal details below</p>
<form action="{{ url_for('login') }}" method="post" data-toggle="validator"> <form action="{{ url_for('login') }}" method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<input type="text" class="form-control" placeholder="First Name" name="firstname" data-error="Please input your first name" required> <input type="text" class="form-control" placeholder="First Name" name="firstname" data-error="Please input your first name" required>
<span class="glyphicon glyphicon-user form-control-feedback"></span> <span class="glyphicon glyphicon-user form-control-feedback"></span>

View file

@ -79,11 +79,9 @@
Edit&nbsp;<i class="fa fa-edit"></i> Edit&nbsp;<i class="fa fa-edit"></i>
</button> </button>
</a> </a>
<a href="{{ url_for('delete_template', template=template.name) }}"> <button type="button" class="btn btn-flat btn-danger button_delete" id="{{template.name}}">
<button type="button" class="btn btn-flat btn-danger button_delete" id="btn_delete"> Delete&nbsp;<i class="fa fa-trash"></i>
Delete&nbsp;<i class="fa fa-trash"></i> </button>
</button>
</a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -111,6 +109,15 @@
"info" : false, "info" : false,
"autoWidth" : false "autoWidth" : false
}); });
// handle delete button
$(document.body).on("click", ".button_delete", function(e) {
var template = $(this).prop('id');
$.post($SCRIPT_ROOT + '/template/' + template + '/delete', { '_csrf_token': '{{ csrf_token() }}' }, function() {
window.location.href = '{{ url_for('templates') }}';
});
});
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}

View file

@ -50,6 +50,7 @@ if errors %}
<!-- form start --> <!-- form start -->
<form role="form" method="post" <form role="form" method="post"
action="{{ url_for('create_template') }}"> action="{{ url_for('create_template') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="box-body"> <div class="box-body">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" name="name" id="name" <input type="text" class="form-control" name="name" id="name"

View file

@ -195,7 +195,7 @@
// following unbind("click") is to avoid multiple times execution // following unbind("click") is to avoid multiple times execution
modal.find('#button_apply_confirm').unbind("click").click(function() { modal.find('#button_apply_confirm').unbind("click").click(function() {
var data = getTableData(table); var data = getTableData(table);
applyChanges(data, '/template/' + template + '/apply', true); applyChanges( {'_csrf_token': '{{ csrf_token() }}', 'records': data}, '/template/' + template + '/apply', true);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');

View file

@ -39,6 +39,7 @@
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane active" id="tabs-personal"> <div class="tab-pane active" id="tabs-personal">
<form role="form" method="post" action="{{ user_profile }}"> <form role="form" method="post" action="{{ user_profile }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group"> <div class="form-group">
<label for="firstname">First Name</label> <input type="text" <label for="firstname">First Name</label> <input type="text"
class="form-control" name="firstname" id="firstname" class="form-control" name="firstname" id="firstname"
@ -62,6 +63,7 @@
<div class="tab-pane" id="tabs-avatar"> <div class="tab-pane" id="tabs-avatar">
<form action="{{ user_profile }}" method="post" <form action="{{ user_profile }}" method="post"
enctype="multipart/form-data"> enctype="multipart/form-data">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group"> <div class="form-group">
<div class="form-group"> <div class="form-group">
<div class="thumbnail" style="width: 200px; height: 210px;"> <div class="thumbnail" style="width: 200px; height: 210px;">
@ -95,6 +97,7 @@
Your account password is managed via LDAP which isn't supported to change here. Your account password is managed via LDAP which isn't supported to change here.
{% else %} {% else %}
<form action="{{ user_profile }}" method="post"> <form action="{{ user_profile }}" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group"> <div class="form-group">
<label for="password">New Password</label> <input <label for="password">New Password</label> <input
type="password" class="form-control" name="password" id="newpassword"/> type="password" class="form-control" name="password" id="newpassword"/>
@ -113,6 +116,7 @@
<!-- {% if session['authentication_type'] in ['LOCAL', 'LDAP'] %} --> <!-- {% if session['authentication_type'] in ['LOCAL', 'LDAP'] %} -->
<div class="tab-pane" id="tabs-authentication"> <div class="tab-pane" id="tabs-authentication">
<form action="{{ user_profile }}" method="post"> <form action="{{ user_profile }}" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group"> <div class="form-group">
<input type="checkbox" id="otp_toggle" class="otp_toggle" {% if current_user.otp_secret %}checked{% endif %}> <input type="checkbox" id="otp_toggle" class="otp_toggle" {% if current_user.otp_secret %}checked{% endif %}>
<label for="otp_toggle">Enable Two Factor Authentication</label> <label for="otp_toggle">Enable Two Factor Authentication</label>
@ -165,7 +169,8 @@
'action' : 'enable_otp', 'action' : 'enable_otp',
'data' : { 'data' : {
'enable_otp' : enable_otp 'enable_otp' : enable_otp
} },
'_csrf_token': '{{ csrf_token() }}'
}; };
applyChanges(postdata, $SCRIPT_ROOT + '/user/profile', false, true); applyChanges(postdata, $SCRIPT_ROOT + '/user/profile', false, true);
}); });

View file

@ -18,7 +18,7 @@ from flask_login import login_user, logout_user, current_user, login_required
from werkzeug import secure_filename from werkzeug import secure_filename
from .models import User, Account, Domain, Record, Role, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord from .models import User, Account, Domain, Record, Role, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord
from app import app, login_manager from app import app, login_manager, csrf
from app.lib import utils from app.lib import utils
from app.oauth import github_oauth, google_oauth, oidc_oauth from app.oauth import github_oauth, google_oauth, oidc_oauth
from app.decorators import admin_role_required, operator_role_required, can_access_domain, can_configure_dnssec, can_create_domain from app.decorators import admin_role_required, operator_role_required, can_access_domain, can_configure_dnssec, can_create_domain
@ -184,7 +184,6 @@ def oidc_login():
else: else:
redirect_uri = url_for('oidc_authorized', _external=True) redirect_uri = url_for('oidc_authorized', _external=True)
return oidc.authorize_redirect(redirect_uri) return oidc.authorize_redirect(redirect_uri)
@app.route('/saml/login') @app.route('/saml/login')
def saml_login(): def saml_login():
@ -215,6 +214,7 @@ def saml_metadata():
@app.route('/saml/authorized', methods=['GET', 'POST']) @app.route('/saml/authorized', methods=['GET', 'POST'])
@csrf.exempt
def saml_authorized(): def saml_authorized():
errors = [] errors = []
if not app.config.get('SAML_ENABLED'): if not app.config.get('SAML_ENABLED'):
@ -710,7 +710,7 @@ def domain_add():
return render_template('domain_add.html', templates=templates, accounts=accounts) return render_template('domain_add.html', templates=templates, accounts=accounts)
@app.route('/admin/domain/<path:domain_name>/delete', methods=['GET']) @app.route('/admin/domain/<path:domain_name>/delete', methods=['POST'])
@login_required @login_required
@operator_role_required @operator_role_required
def domain_delete(domain_name): def domain_delete(domain_name):
@ -864,22 +864,6 @@ def record_update(domain_name):
return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500) return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500)
@app.route('/domain/<path:domain_name>/record/<path:record_name>/type/<path:record_type>/delete', methods=['GET'])
@login_required
@operator_role_required
def record_delete(domain_name, record_name, record_type):
try:
r = Record(name=record_name, type=record_type)
result = r.delete(domain=domain_name)
if result['status'] == 'error':
print(result['msg'])
except Exception as e:
logging.error('Cannot delete record. Error: {0}'.format(e))
logging.debug(traceback.format_exc())
return redirect(url_for('error', code=500)), 500
return redirect(url_for('domain', domain_name=domain_name))
@app.route('/domain/<path:domain_name>/info', methods=['GET']) @app.route('/domain/<path:domain_name>/info', methods=['GET'])
@login_required @login_required
@can_access_domain @can_access_domain
@ -898,7 +882,7 @@ def domain_dnssec(domain_name):
return make_response(jsonify(dnssec), 200) return make_response(jsonify(dnssec), 200)
@app.route('/domain/<path:domain_name>/dnssec/enable', methods=['GET']) @app.route('/domain/<path:domain_name>/dnssec/enable', methods=['POST'])
@login_required @login_required
@can_access_domain @can_access_domain
@can_configure_dnssec @can_configure_dnssec
@ -908,7 +892,7 @@ def domain_dnssec_enable(domain_name):
return make_response(jsonify(dnssec), 200) return make_response(jsonify(dnssec), 200)
@app.route('/domain/<path:domain_name>/dnssec/disable', methods=['GET']) @app.route('/domain/<path:domain_name>/dnssec/disable', methods=['POST'])
@login_required @login_required
@can_access_domain @can_access_domain
@can_configure_dnssec @can_configure_dnssec
@ -1097,7 +1081,7 @@ def apply_records(template):
jdata = request.json jdata = request.json
records = [] records = []
for j in jdata: for j in jdata['records']:
name = '@' if j['record_name'] in ['@', ''] else j['record_name'] name = '@' if j['record_name'] in ['@', ''] else j['record_name']
type = j['record_type'] type = j['record_type']
data = j['record_data'] data = j['record_data']
@ -1121,7 +1105,7 @@ def apply_records(template):
return make_response(jsonify({'status': 'error', 'msg': 'Error when applying new changes'}), 500) return make_response(jsonify({'status': 'error', 'msg': 'Error when applying new changes'}), 500)
@app.route('/template/<path:template>/delete', methods=['GET']) @app.route('/template/<path:template>/delete', methods=['POST'])
@login_required @login_required
@operator_role_required @operator_role_required
def delete_template(template): def delete_template(template):
@ -1418,7 +1402,7 @@ def admin_setting_basic():
'allow_user_create_domain', 'allow_user_create_domain',
'bg_domain_updates', 'bg_domain_updates',
'site_name', 'site_name',
'session_timeout' ] 'session_timeout' ]
return render_template('admin_setting_basic.html', settings=settings) return render_template('admin_setting_basic.html', settings=settings)
@ -1638,6 +1622,7 @@ def qrcode():
@app.route('/nic/checkip.html', methods=['GET', 'POST']) @app.route('/nic/checkip.html', methods=['GET', 'POST'])
@csrf.exempt
def dyndns_checkip(): def dyndns_checkip():
# route covers the default ddclient 'web' setting for the checkip service # route covers the default ddclient 'web' setting for the checkip service
return render_template('dyndns.html', response=request.environ.get('HTTP_X_REAL_IP', request.remote_addr)) return render_template('dyndns.html', response=request.environ.get('HTTP_X_REAL_IP', request.remote_addr))
@ -1645,6 +1630,7 @@ def dyndns_checkip():
@app.route('/nic/update', methods=['GET', 'POST']) @app.route('/nic/update', methods=['GET', 'POST'])
@dyndns_login_required @dyndns_login_required
@csrf.exempt
def dyndns_update(): def dyndns_update():
# dyndns protocol response codes in use are: # dyndns protocol response codes in use are:
# good: update successful # good: update successful

View file

@ -2,7 +2,6 @@ import os
basedir = os.path.abspath(os.path.dirname(__file__)) basedir = os.path.abspath(os.path.dirname(__file__))
# BASIC APP CONFIG # BASIC APP CONFIG
WTF_CSRF_ENABLED = True
SECRET_KEY = 'We are the world' SECRET_KEY = 'We are the world'
BIND_ADDRESS = '127.0.0.1' BIND_ADDRESS = '127.0.0.1'
PORT = 9191 PORT = 9191

View file

@ -2,7 +2,6 @@ import os
basedir = os.path.abspath(os.path.dirname(__file__)) basedir = os.path.abspath(os.path.dirname(__file__))
# BASIC APP CONFIG # BASIC APP CONFIG
WTF_CSRF_ENABLED = True
SECRET_KEY = 'changeme' SECRET_KEY = 'changeme'
LOG_LEVEL = 'DEBUG' LOG_LEVEL = 'DEBUG'
LOG_FILE = 'logs/log.txt' LOG_FILE = 'logs/log.txt'

View file

@ -19,3 +19,4 @@ pytz>=2017.3
cssmin==0.2.0 cssmin==0.2.0
jsmin==2.2.2 jsmin==2.2.2
Authlib==0.10 Authlib==0.10
Flask-Seasurf