mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-11-09 15:10:27 +00:00
Check zone serial before allowing user to submit their change. #183
This commit is contained in:
parent
84d4bfaed0
commit
52b6966c83
@ -7,6 +7,10 @@ app = Flask(__name__)
|
||||
app.config.from_object('config')
|
||||
app.wsgi_app = ProxyFix(app.wsgi_app)
|
||||
|
||||
#### CONFIGURE LOGGER ####
|
||||
from app.lib.log import logger
|
||||
logging = logger('powerdns-admin', app.config['LOG_LEVEL'], app.config['LOG_FILE']).config()
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
db = SQLAlchemy(app)
|
||||
|
@ -10,6 +10,7 @@ import pyotp
|
||||
import re
|
||||
import dns.reversename
|
||||
import sys
|
||||
import logging as logger
|
||||
|
||||
from datetime import datetime
|
||||
from urllib.parse import urljoin
|
||||
@ -19,10 +20,8 @@ from flask_login import AnonymousUserMixin
|
||||
|
||||
from app import app, db
|
||||
from app.lib import utils
|
||||
from app.lib.log import logger
|
||||
|
||||
# LOG CONFIGS
|
||||
logging = logger('MODEL', app.config['LOG_LEVEL'], app.config['LOG_FILE']).config()
|
||||
logging = logger.getLogger(__name__)
|
||||
|
||||
if 'LDAP_TYPE' in app.config.keys():
|
||||
LDAP_URI = app.config['LDAP_URI']
|
||||
@ -510,24 +509,18 @@ class Domain(db.Model):
|
||||
logging.error('Can not create setting {0} for domain {1}. {2}'.format(setting, self.name, e))
|
||||
return False
|
||||
|
||||
def get_domain_info(self, domain_name):
|
||||
"""
|
||||
Get all domains which has in PowerDNS
|
||||
"""
|
||||
headers = {}
|
||||
headers['X-API-Key'] = PDNS_API_KEY
|
||||
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain_name)), headers=headers)
|
||||
return jdata
|
||||
|
||||
def get_domains(self):
|
||||
"""
|
||||
Get all domains which has in PowerDNS
|
||||
jdata example:
|
||||
[
|
||||
{
|
||||
"id": "example.org.",
|
||||
"url": "/servers/localhost/zones/example.org.",
|
||||
"name": "example.org",
|
||||
"kind": "Native",
|
||||
"dnssec": false,
|
||||
"account": "",
|
||||
"masters": [],
|
||||
"serial": 2015101501,
|
||||
"notified_serial": 0,
|
||||
"last_check": 0
|
||||
}
|
||||
]
|
||||
"""
|
||||
headers = {}
|
||||
headers['X-API-Key'] = PDNS_API_KEY
|
||||
@ -886,6 +879,7 @@ class Domain(db.Model):
|
||||
else:
|
||||
return {'status': 'error', 'msg': 'This domain doesnot exist'}
|
||||
|
||||
|
||||
class DomainUser(db.Model):
|
||||
__tablename__ = 'domain_user'
|
||||
id = db.Column(db.Integer, primary_key = True)
|
||||
@ -1183,6 +1177,7 @@ class Record(object):
|
||||
return {'status': 'error', 'msg': jdata2['error']}
|
||||
else:
|
||||
self.auto_ptr(domain, new_records, deleted_records)
|
||||
self.update_db_serial(domain)
|
||||
logging.info('Record was applied successfully.')
|
||||
return {'status': 'ok', 'msg': 'Record was applied successfully'}
|
||||
except Exception as e:
|
||||
@ -1335,6 +1330,20 @@ class Record(object):
|
||||
logging.error("Cannot add record {0}/{1}/{2} to domain {3}. DETAIL: {4}".format(self.name, self.type, self.data, domain, e))
|
||||
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
|
||||
|
||||
def update_db_serial(self, domain):
|
||||
headers = {}
|
||||
headers['X-API-Key'] = PDNS_API_KEY
|
||||
jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain)), headers=headers, method='GET')
|
||||
serial = jdata['serial']
|
||||
|
||||
domain = Domain.query.filter(Domain.name==domain).first()
|
||||
if domain:
|
||||
domain.serial = serial
|
||||
db.session.commit()
|
||||
return {'status': True, 'msg': 'Synced local serial for domain name {0}'.format(domain)}
|
||||
else:
|
||||
return {'status': False, 'msg': 'Could not find domain name {0} in local db'.format(domain)}
|
||||
|
||||
|
||||
class Server(object):
|
||||
"""
|
||||
|
@ -30,6 +30,37 @@ function applyChanges(data, url, showResult, refreshPage) {
|
||||
});
|
||||
}
|
||||
|
||||
function applyRecordChanges(data, domain) {
|
||||
var success = false;
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
url : $SCRIPT_ROOT + '/domain/' + domain + '/apply',
|
||||
data : JSON.stringify(data),// now data come in this function
|
||||
contentType : "application/json; charset=utf-8",
|
||||
crossDomain : true,
|
||||
dataType : "json",
|
||||
success : function(data, status, jqXHR) {
|
||||
// update Apply button value
|
||||
$.getJSON($SCRIPT_ROOT + '/domain/' + domain + '/info', function(data) {
|
||||
$(".button_apply_changes").val(data['serial']);
|
||||
});
|
||||
|
||||
console.log("Applied changes successfully.")
|
||||
var modal = $("#modal_success");
|
||||
modal.find('.modal-body p').text("Applied changes successfully");
|
||||
modal.modal('show');
|
||||
},
|
||||
|
||||
error : function(jqXHR, status) {
|
||||
console.log(jqXHR);
|
||||
var modal = $("#modal_error");
|
||||
var responseJson = jQuery.parseJSON(jqXHR.responseText);
|
||||
modal.find('.modal-body p').text(responseJson['msg']);
|
||||
modal.modal('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getTableData(table) {
|
||||
var rData = []
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
<button type="button" class="btn btn-flat btn-primary pull-left button_add_record" id="{{ domain.name }}">
|
||||
Add Record <i class="fa fa-plus"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-flat btn-primary pull-right button_apply_changes" id="{{ domain.name }}">
|
||||
<button type="button" class="btn btn-flat btn-primary pull-right button_apply_changes" id="{{ domain.name }}" value="{{ domain.serial }}">
|
||||
Apply Changes <i class="fa fa-floppy-o"></i>
|
||||
</button>
|
||||
{% else %}
|
||||
@ -196,11 +196,14 @@
|
||||
var modal = $("#modal_apply_changes");
|
||||
var table = $("#tbl_records").DataTable();
|
||||
var domain = $(this).prop('id');
|
||||
var serial = $(".button_apply_changes").val();
|
||||
var info = "Are you sure you want to apply your changes?";
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_apply_confirm').click(function() {
|
||||
var data = getTableData(table);
|
||||
applyChanges(data, $SCRIPT_ROOT + '/domain/' + domain + '/apply', true);
|
||||
|
||||
// following unbind("click") is to avoid multiple times execution
|
||||
modal.find('#button_apply_confirm').unbind("click").click(function() {
|
||||
var data = {'serial': serial, 'record': getTableData(table)};
|
||||
applyRecordChanges(data, domain);
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
|
35
app/views.py
35
app/views.py
@ -1,5 +1,6 @@
|
||||
import base64
|
||||
import json
|
||||
import logging as logger
|
||||
import os
|
||||
import traceback
|
||||
import re
|
||||
@ -19,15 +20,13 @@ from werkzeug.security import gen_salt
|
||||
from .models import User, Domain, Record, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord
|
||||
from app import app, login_manager, github, google
|
||||
from app.lib import utils
|
||||
from app.lib.log import logger
|
||||
from app.decorators import admin_role_required, can_access_domain
|
||||
|
||||
if app.config['SAML_ENABLED']:
|
||||
from onelogin.saml2.auth import OneLogin_Saml2_Auth
|
||||
from onelogin.saml2.utils import OneLogin_Saml2_Utils
|
||||
|
||||
# LOG CONFIG
|
||||
logging = logger('MODEL', app.config['LOG_LEVEL'], app.config['LOG_FILE']).config()
|
||||
logging = logger.getLogger(__name__)
|
||||
|
||||
# FILTERS
|
||||
jinja2.filters.FILTERS['display_record_name'] = utils.display_record_name
|
||||
@ -541,7 +540,6 @@ def dashboard_domains():
|
||||
|
||||
|
||||
@app.route('/domain/<path:domain_name>', methods=['GET', 'POST'])
|
||||
@app.route('/domain', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@can_access_domain
|
||||
def domain(domain_name):
|
||||
@ -580,7 +578,7 @@ def domain(domain_name):
|
||||
editable_records = app.config['FORWARD_RECORDS_ALLOW_EDIT']
|
||||
else:
|
||||
editable_records = app.config['REVERSE_RECORDS_ALLOW_EDIT']
|
||||
return render_template('domain.html', domain=domain, records=records, editable_records=editable_records,pdns_version=app.config['PDNS_VERSION'])
|
||||
return render_template('domain.html', domain=domain, records=records, editable_records=editable_records, pdns_version=app.config['PDNS_VERSION'])
|
||||
|
||||
|
||||
@app.route('/admin/domain/add', methods=['GET', 'POST'])
|
||||
@ -593,7 +591,6 @@ def domain_add():
|
||||
domain_name = request.form.getlist('domain_name')[0]
|
||||
domain_type = request.form.getlist('radio_type')[0]
|
||||
domain_template = request.form.getlist('domain_template')[0]
|
||||
logging.info("Selected template ==== {0}".format(domain_template))
|
||||
soa_edit_api = request.form.getlist('radio_type_soa_edit_api')[0]
|
||||
|
||||
if ' ' in domain_name or not domain_name or not domain_type:
|
||||
@ -716,11 +713,26 @@ def record_apply(domain_name):
|
||||
example jdata: {u'record_ttl': u'1800', u'record_type': u'CNAME', u'record_name': u'test4', u'record_status': u'Active', u'record_data': u'duykhanh.me'}
|
||||
"""
|
||||
#TODO: filter removed records / name modified records.
|
||||
|
||||
try:
|
||||
jdata = request.json
|
||||
|
||||
submitted_serial = jdata['serial']
|
||||
submitted_record = jdata['record']
|
||||
|
||||
domain = Domain.query.filter(Domain.name==domain_name).first()
|
||||
|
||||
logging.debug('Your submitted serial: {0}'.format(submitted_serial))
|
||||
logging.debug('Current domain serial: {0}'.format(domain.serial))
|
||||
|
||||
if domain:
|
||||
if int(submitted_serial) != domain.serial:
|
||||
return make_response(jsonify( {'status': 'error', 'msg': 'The zone has been changed by another session or user. Please refresh this web page to load updated records.'} ), 500)
|
||||
else:
|
||||
return make_response(jsonify( {'status': 'error', 'msg': 'Domain name {0} does not exist'.format(domain_name)} ), 404)
|
||||
|
||||
r = Record()
|
||||
result = r.apply(domain_name, jdata)
|
||||
result = r.apply(domain_name, submitted_record)
|
||||
if result['status'] == 'ok':
|
||||
history = History(msg='Apply record changes to domain {0}'.format(domain_name), detail=str(jdata), created_by=current_user.username)
|
||||
history.add()
|
||||
@ -770,6 +782,15 @@ def record_delete(domain_name, record_name, record_type):
|
||||
return redirect(url_for('domain', domain_name=domain_name))
|
||||
|
||||
|
||||
@app.route('/domain/<path:domain_name>/info', methods=['GET'])
|
||||
@login_required
|
||||
@can_access_domain
|
||||
def domain_info(domain_name):
|
||||
domain = Domain()
|
||||
domain_info = domain.get_domain_info(domain_name)
|
||||
return make_response(jsonify(domain_info), 200)
|
||||
|
||||
|
||||
@app.route('/domain/<path:domain_name>/dnssec', methods=['GET'])
|
||||
@login_required
|
||||
@can_access_domain
|
||||
|
Loading…
Reference in New Issue
Block a user