Merge and resolve the conflicts from master

This commit is contained in:
Khanh Ngo 2018-06-13 09:35:19 +07:00
commit 8a22e030cd
No known key found for this signature in database
GPG Key ID: B9AE3BAF6D5A7B22
11 changed files with 703 additions and 25 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
app/static/* linguist-vendored

View File

@ -1,9 +1,17 @@
from werkzeug.contrib.fixers import ProxyFix
from flask import Flask, request, session, redirect, url_for
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy import SQLAlchemy as SA
from flask_migrate import Migrate
# subclass SQLAlchemy to enable pool_pre_ping
class SQLAlchemy(SA):
def apply_pool_defaults(self, app, options):
SA.apply_pool_defaults(self, app, options)
options["pool_pre_ping"] = True
app = Flask(__name__)
app.config.from_object('config')
app.wsgi_app = ProxyFix(app.wsgi_app)

View File

@ -355,25 +355,48 @@ class User(db.Model):
db.session.rollback()
return False
def get_account_query(self):
"""
Get query for account to which the user is associated.
"""
return db.session.query(Account) \
.outerjoin(AccountUser, Account.id==AccountUser.account_id) \
.filter(AccountUser.user_id==self.id)
def get_account(self):
"""
Get all accounts to which the user is associated.
"""
return self.get_account_query()
def get_domain_query(self):
return db.session.query(User, DomainUser, Domain) \
.filter(User.id == self.id) \
.filter(User.id == DomainUser.user_id) \
.filter(Domain.id == DomainUser.domain_id)
"""
Get query for domain to which the user has access permission.
This includes direct domain permission AND permission through
account membership
"""
return db.session.query(Domain) \
.outerjoin(DomainUser, Domain.id==DomainUser.domain_id) \
.outerjoin(Account, Domain.account_id==Account.id) \
.outerjoin(AccountUser, Account.id==AccountUser.account_id) \
.filter(db.or_(DomainUser.user_id==User.id, AccountUser.user_id==User.id)) \
.filter(User.id==self.id)
def get_domain(self):
"""
Get domains which user has permission to
access
"""
return [q[2] for q in self.get_domain_query()]
return self.get_domain_query()
def delete(self):
"""
Delete a user
"""
# revoke all user privileges first
# revoke all user privileges and account associations first
self.revoke_privilege()
for a in self.get_account():
a.revoke_privileges_by_id(self.id)
try:
User.query.filter(User.username == self.username).delete()
@ -426,6 +449,161 @@ class User(db.Model):
return False
class Account(db.Model):
__tablename__ = 'account'
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(40), index=True, unique=True, nullable=False)
description = db.Column(db.String(128))
contact = db.Column(db.String(128))
mail = db.Column(db.String(128))
domains = db.relationship("Domain", back_populates="account")
def __init__(self, name=None, description=None, contact=None, mail=None):
self.name = name
self.description = description
self.contact = contact
self.mail = mail
if self.name is not None:
self.name = ''.join(c for c in self.name.lower() if c in "abcdefghijklmnopqrstuvwxyz0123456789")
def __repr__(self):
return '<Account {0}r>'.format(self.name)
def get_name_by_id(self, account_id):
"""
Convert account_id to account_name
"""
account = Account.query.filter(Account.id == account_id).first()
if account is None:
return ''
return account.name
def get_id_by_name(self, account_name):
"""
Convert account_name to account_id
"""
account = Account.query.filter(Account.name == account_name).first()
if account is None:
return None
return account.id
def unassociate_domains(self):
"""
Remove associations to this account from all domains
"""
account = Account.query.filter(Account.name == self.name).first()
for domain in account.domains:
domain.assoc_account(None)
def create_account(self):
"""
Create a new account
"""
# Sanity check - account name
if self.name == "":
return {'status': False, 'msg': 'No account name specified'}
# check that account name is not already used
account = Account.query.filter(Account.name == self.name).first()
if account:
return {'status': False, 'msg': 'Account already exists'}
db.session.add(self)
db.session.commit()
return {'status': True, 'msg': 'Account created successfully'}
def update_account(self):
"""
Update an existing account
"""
# Sanity check - account name
if self.name == "":
return {'status': False, 'msg': 'No account name specified'}
# read account and check that it exists
account = Account.query.filter(Account.name == self.name).first()
if not account:
return {'status': False, 'msg': 'Account does not exist'}
account.description = self.description
account.contact = self.contact
account.mail = self.mail
db.session.commit()
return {'status': True, 'msg': 'Account updated successfully'}
def delete_account(self):
"""
Delete an account
"""
# unassociate all domains and users first
self.unassociate_domains()
self.grant_privileges([])
try:
Account.query.filter(Account.name == self.name).delete()
db.session.commit()
return True
except:
db.session.rollback()
logging.error('Cannot delete account {0} from DB'.format(self.username))
return False
def get_user(self):
"""
Get users (id) associated with this account
"""
user_ids = []
query = db.session.query(AccountUser, Account).filter(User.id==AccountUser.user_id).filter(Account.id==AccountUser.account_id).filter(Account.name==self.name).all()
for q in query:
user_ids.append(q[0].user_id)
return user_ids
def grant_privileges(self, new_user_list):
"""
Reconfigure account_user table
"""
account_id = self.get_id_by_name(self.name)
account_user_ids = self.get_user()
new_user_ids = [u.id for u in User.query.filter(User.username.in_(new_user_list)).all()] if new_user_list else []
removed_ids = list(set(account_user_ids).difference(new_user_ids))
added_ids = list(set(new_user_ids).difference(account_user_ids))
try:
for uid in removed_ids:
AccountUser.query.filter(AccountUser.user_id == uid).filter(AccountUser.account_id==account_id).delete()
db.session.commit()
except:
db.session.rollback()
logging.error('Cannot revoke user privielges on account {0}'.format(self.name))
try:
for uid in added_ids:
au = AccountUser(account_id, uid)
db.session.add(au)
db.session.commit()
except:
db.session.rollback()
logging.error('Cannot grant user privileges to account {0}'.format(self.name))
def revoke_privileges_by_id(self, user_id):
"""
Remove a single user from prigilege list based on user_id
"""
new_uids = [u for u in self.get_user() if u != user_id]
users = []
for uid in new_uids:
users.append(User(id=uid).get_user_info_by_id().username)
self.grant_privileges(users)
class Role(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(64), index=True, unique=True)
@ -446,6 +624,7 @@ class Role(db.Model):
def __repr__(self):
return '<Role {0}r>'.format(self.name)
class DomainSetting(db.Model):
__tablename__ = 'domain_setting'
id = db.Column(db.Integer, primary_key = True)
@ -485,9 +664,11 @@ class Domain(db.Model):
notified_serial = db.Column(db.Integer)
last_check = db.Column(db.Integer)
dnssec = db.Column(db.Integer)
account_id = db.Column(db.Integer, db.ForeignKey('account.id'))
account = db.relationship("Account", back_populates="domains")
settings = db.relationship('DomainSetting', back_populates='domain')
def __init__(self, id=None, name=None, master=None, type='NATIVE', serial=None, notified_serial=None, last_check=None, dnssec=None):
def __init__(self, id=None, name=None, master=None, type='NATIVE', serial=None, notified_serial=None, last_check=None, dnssec=None, account_id=None):
self.id = id
self.name = name
self.master = master
@ -496,6 +677,7 @@ class Domain(db.Model):
self.notified_serial = notified_serial
self.last_check = last_check
self.dnssec = dnssec
self.account_id = account_id
def __repr__(self):
return '<Domain {0}>'.format(self.name)
@ -575,6 +757,7 @@ class Domain(db.Model):
# update/add new domain
for data in jdata:
account_id = Account().get_id_by_name(data['account'])
d = dict_db_domain.get(data['name'].rstrip('.'), None)
changed = False
if d:
@ -584,7 +767,8 @@ class Domain(db.Model):
or d.serial != data['serial']
or d.notified_serial != data['notified_serial']
or d.last_check != ( 1 if data['last_check'] else 0 )
or d.dnssec != data['dnssec'] ):
or d.dnssec != data['dnssec']
or d.account_id != account_id ):
d.master = str(data['masters'])
d.type = data['kind']
@ -592,6 +776,7 @@ class Domain(db.Model):
d.notified_serial = data['notified_serial']
d.last_check = 1 if data['last_check'] else 0
d.dnssec = 1 if data['dnssec'] else 0
d.account_id = account_id
changed = True
else:
@ -604,19 +789,20 @@ class Domain(db.Model):
d.notified_serial = data['notified_serial']
d.last_check = data['last_check']
d.dnssec = 1 if data['dnssec'] else 0
d.account_id = account_id
db.session.add(d)
changed = True
if changed:
try:
db.session.commit()
except:
except Exception as e:
db.session.rollback()
return {'status': 'ok', 'msg': 'Domain table has been updated successfully'}
except Exception as e:
logging.error('Can not update domain table. Error: {0}'.format(e))
return {'status': 'error', 'msg': 'Can not update domain table'}
def add(self, domain_name, domain_type, soa_edit_api, domain_ns=[], domain_master_ips=[]):
def add(self, domain_name, domain_type, soa_edit_api, domain_ns=[], domain_master_ips=[], account_name=None):
"""
Add a domain to power dns
"""
@ -638,7 +824,8 @@ class Domain(db.Model):
"kind": domain_type,
"masters": domain_master_ips,
"nameservers": domain_ns,
"soa_edit_api": soa_edit_api
"soa_edit_api": soa_edit_api,
"account": account_name
}
try:
@ -647,6 +834,7 @@ class Domain(db.Model):
logging.error(jdata['error'])
return {'status': 'error', 'msg': jdata['error']}
else:
self.update()
logging.info('Added domain {0} successfully'.format(domain_name))
return {'status': 'ok', 'msg': 'Added domain successfully'}
except Exception as e:
@ -798,7 +986,6 @@ class Domain(db.Model):
db.session.rollback()
logging.error('Cannot grant user privielges to domain {0}'.format(self.name))
def update_from_master(self, domain_name):
"""
Update records from Master DNS server
@ -900,6 +1087,60 @@ class Domain(db.Model):
else:
return {'status': 'error', 'msg': 'This domain doesnot exist'}
def assoc_account(self, account_id):
"""
Associate domain with a domain, specified by account id
"""
domain_name = self.name
# Sanity check - domain name
if domain_name == "":
return {'status': False, 'msg': 'No domain name specified'}
# read domain and check that it exists
domain = Domain.query.filter(Domain.name == domain_name).first()
if not domain:
return {'status': False, 'msg': 'Domain does not exist'}
headers = {}
headers['X-API-Key'] = PDNS_API_KEY
account_name = Account().get_name_by_id(account_id)
post_data = {
"account": account_name
}
try:
jdata = utils.fetch_json(
urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/{0}'.format(domain_name)), headers=headers,
method='PUT', data=post_data)
if 'error' in jdata.keys():
logging.error(jdata['error'])
return {'status': 'error', 'msg': jdata['error']}
else:
self.update()
logging.info('account changed for domain {0} successfully'.format(domain_name))
return {'status': 'ok', 'msg': 'account changed successfully'}
except Exception as e:
logging.debug(e)
logging.debug(traceback.format_exc())
logging.error('Cannot change account for domain {0}'.format(domain_name))
return {'status': 'error', 'msg': 'Cannot change account for this domain.'}
return {'status': True, 'msg': 'Domain association successful'}
def get_account(self):
"""
Get current account associated with this domain
"""
domain = Domain.query.filter(Domain.name == self.name).first()
return domain.account
class DomainUser(db.Model):
__tablename__ = 'domain_user'
@ -915,6 +1156,20 @@ class DomainUser(db.Model):
return '<Domain_User {0} {1}>'.format(self.domain_id, self.user_id)
class AccountUser(db.Model):
__tablename__ = 'account_user'
id = db.Column(db.Integer, primary_key = True)
account_id = db.Column(db.Integer, db.ForeignKey('account.id'), nullable = False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable = False)
def __init__(self, account_id, user_id):
self.account_id = account_id
self.user_id = user_id
def __repr__(self):
return '<Account_User {0} {1}>'.format(self.account_id, self.user_id)
class Record(object):
"""
This is not a model, it's just an object

View File

@ -0,0 +1,118 @@
{% extends "base.html" %}
{% block title %}<title>DNS Control Panel - Edit Account</title>{% endblock %}
{% block dashboard_stat %}
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
Account
<small>{% if create %}New account{% else %}{{ account.name }}{% endif %}</small>
</h1>
<ol class="breadcrumb">
<li><a href="{{ url_for('dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
<li><a href="{{ url_for('admin_manageaccount') }}">Accounts</a></li>
<li class="active">{% if create %}Add{% else %}Edit{% endif %} account</li>
</ol>
</section>
{% endblock %}
{% block content %}
<section class="content">
<div class="row">
<div class="col-md-4">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">{% if create %}Add{% else %}Edit{% endif %} account</h3>
</div>
<!-- /.box-header -->
<!-- form start -->
<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="create" value="{{ create }}">
<div class="box-body">
{% if error %}
<div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<h4><i class="icon fa fa-ban"></i> Error!</h4>
{{ error }}
</div>
<span class="help-block">{{ error }}</span>
{% endif %}
<div class="form-group has-feedback {% if invalid_accountname or duplicate_accountname %}has-error{% endif %}">
<label class="control-label" for="accountname">Name</label>
<input type="text" class="form-control" placeholder="Account Name (required)"
name="accountname" {% if account %}value="{{ account.name }}"{% endif %} {% if not create %}disabled{% endif %}>
<span class="fa fa-cog form-control-feedback"></span>
{% if invalid_accountname %}
<span class="help-block">Cannot be blank and must only contain alphanumeric characters.</span>
{% elif duplicate_accountname %}
<span class="help-block">Account name already in use.</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<label class="control-label" for="accountdescription">Description</label>
<input type="text" class="form-control" placeholder="Account Description (optional)"
name="accountdescription" {% if account %}value="{{ account.description }}"{% endif %}>
<span class="fa fa-industry form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="accountcontact">Contact Person</label>
<input type="text" class="form-control" placeholder="Contact Person (optional)"
name="accountcontact" {% if account %}value="{{ account.contact }}"{% endif %}>
<span class="fa fa-user form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="accountmail">Mail Address</label>
<input type="email" class="form-control" placeholder="Mail Address (optional)"
name="accountmail" {% if account %}value="{{ account.mail }}"{% endif %}>
<span class="fa fa-envelope form-control-feedback"></span>
</div>
</div>
<div class="box-header with-border">
<h3 class="box-title">Access Control</h3>
</div>
<div class="box-body">
<p>Users on the right have access to manage records in all domains
associated with the account.</p>
<p>Click on users to move between columns.</p>
<div class="form-group col-xs-2">
<select multiple="multiple" class="form-control" id="account_multi_user" name="account_multi_user">
{% for user in users %}
<option {% if user.id in account_user_ids %}selected{% endif %} value="{{ user.username }}">{{ user.username }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-flat btn-primary">{% if create %}Create{% else %}Update{% endif %} Account</button>
</div>
</form>
</div>
</div>
<div class="col-md-8">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Help with creating a new account</h3>
</div>
<div class="box-body">
<p>
An account allows grouping of domains belonging to a particular entity, such as a customer or department.<br/>
A domain can be assigned to an account upon domain creation or through the domain administration page.
</p>
<p>Fill in all the fields to the in the form to the left.</p>
<p>
<strong>Name</strong> is an account identifier. It will be stored as all lowercase letters (no spaces, special characters etc).<br/>
<strong>Description</strong> is a user friendly name for this account.<br/>
<strong>Contact person</strong> is the name of a contact person at the account.<br/>
<strong>Mail Address</strong> is an e-mail address for the contact person.
</p>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block extrascripts %}
<script>
$("#account_multi_user").multiSelect();
</script>
{% endblock %}

View File

@ -0,0 +1,127 @@
{% extends "base.html" %} {% block title %}
<title>DNS Control Panel - Account Management</title>
{% endblock %} {% block dashboard_stat %}
<section class="content-header">
<h1>
Accounts <small>Manage accounts</small>
</h1>
<ol class="breadcrumb">
<li><a href="{{ url_for('dashboard') }}"><i
class="fa fa-dashboard"></i> Home</a></li>
<li class="active">Accounts</li>
</ol>
</section>
{% endblock %} {% block content %}
<section class="content">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<h3 class="box-title">Account Management</h3>
</div>
<div class="box-body">
<a href="{{ url_for('admin_editaccount') }}">
<button type="button" class="btn btn-flat btn-primary pull-left button_add_account">
Add Account&nbsp;<i class="fa fa-plus"></i>
</button>
</a>
</div>
<div class="box-body">
<table id="tbl_accounts" class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Contact</th>
<th>Mail</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for account in accounts %}
<tr class="odd gradeX">
<td>{{ account.name }}</td>
<td>{{ account.description }}</td>
<td>{{ account.contact }}</td>
<td>{{ account.mail }}</td>
<td width="15%">
<button type="button" class="btn btn-flat btn-success" onclick="window.location.href='{{ url_for('admin_editaccount', account_name=account.name) }}'">
Edit&nbsp;<i class="fa fa-cog"></i>
</button>
<button type="button" class="btn btn-flat btn-danger button_delete" id="{{ account.name }}">
Delete&nbsp;<i class="fa fa-trash"></i>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</section>
{% endblock %}
{% block extrascripts %}
<script>
// set up accounts data table
$("#tbl_accounts").DataTable({
"paging" : true,
"lengthChange" : true,
"searching" : true,
"ordering" : true,
"columnDefs": [
{ "orderable": false, "targets": [-1] }
],
"info" : false,
"autoWidth" : false,
"lengthMenu": [ [10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"]],
"pageLength": 10
});
// handle deletion of account
$(document.body).on('click', '.button_delete', function() {
var modal = $("#modal_delete");
var accountname = $(this).prop('id');
var info = "Are you sure you want to delete " + accountname + "?";
modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function() {
var postdata = {'action': 'delete_account', 'data': accountname}
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manageaccount', false, true);
modal.modal('hide');
})
modal.modal('show');
});
</script>
{% endblock %}
{% block modals %}
<div class="modal fade modal-warning" id="modal_delete">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Confirmation</h4>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-flat btn-default pull-left"
data-dismiss="modal">Close</button>
<button type="button" class="btn btn-flat btn-danger" id="button_delete_confirm">Delete</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
{% endblock %}

View File

@ -129,6 +129,7 @@
<li><a href="{{ url_for('admin') }}"><i class="fa fa-wrench"></i> <span>Admin Console</span></a></li>
<li><a href="{{ url_for('templates') }}"><i class="fa fa-clone"></i> <span>Domain Templates</span></a></li>
<li><a href="{{ url_for('admin_manageuser') }}"><i class="fa fa-users"></i> <span>Users</span></a></li>
<li><a href="{{ url_for('admin_manageaccount') }}"><i class="fa fa-industry"></i> <span>Accounts</span></a></li>
<li><a href="{{ url_for('admin_history') }}"><i class="fa fa-calendar"></i> <span>History</span></a></li>
<li><a href="{{ url_for('admin_settings') }}"><i class="fa fa-cog"></i> <span>Settings</span></a></li>
{% endif %}

View File

@ -134,6 +134,7 @@
<th>Type</th>
<th>Serial</th>
<th>Master</th>
<th>Account</th>
<th {% if current_user.role.name !='Administrator' %}width="6%"{% else %}width="25%"{% endif %}>Action</th>
</tr>
</thead>
@ -178,6 +179,10 @@
"lengthChange" : true,
"searching" : true,
"ordering" : true,
"columnDefs": [
{ "orderable": false, "targets": [-1] }
{% if current_user.role.name != 'Administrator' %},{ "visible": false, "targets": [-2] }{% endif %}
],
"processing" : true,
"serverSide" : true,
"ajax" : "{{ url_for('dashboard_domains') }}",

View File

@ -22,6 +22,12 @@
{% if domain.master == '[]'%}N/A{% else %}{{ domain.master|display_master_name }}{% endif %}
{% endmacro %}
{% macro account(domain) %}
{% if current_user.role.name =='Administrator' %}
{% if domain.account_description != "" %}{{ domain.account.description }} {% endif %}[{{ domain.account.name }}]
{% endif %}
{% endmacro %}
{% macro actions(domain) %}
{% if current_user.role.name =='Administrator' %}
<td width="25%">

View File

@ -31,6 +31,12 @@
<div class="form-group">
<input type="text" class="form-control" name="domain_name" id="domain_name" placeholder="Enter a valid domain name (required)">
</div>
<select name="accountid" class="form-control" style="width:15em;">
<option value="0">- No Account -</option>
{% for account in accounts %}
<option value="{{ account.id }}">{{ account.name }}</option>
{% endfor %}
</select><br/>
<div class="form-group">
<label>Type</label>
<div class="radio">

View File

@ -71,6 +71,32 @@
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<h3 class="box-title">Account</h3>
</div>
<div class="box-body">
<div class="col-xs-12">
<div class="form-group">
<form method="post" action="{{ url_for('domain_change_account', domain_name=domain.name) }}">
<select name="accountid" class="form-control" style="width:15em;">
<option value="0">- No Account -</option>
{% for account in accounts %}
<option value="{{ account.id }}" {% if domain_account.id == account.id %}selected{% endif %}>{{ account.name }}</option>
{% endfor %}
</select><br/>
<button type="submit" class="btn btn-flat btn-primary" id="change_soa_edit_api">
<i class="fa fa-check"></i>&nbsp;Change account for {{ domain.name }}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box">

View File

@ -17,7 +17,7 @@ from flask_login import login_user, logout_user, current_user, login_required
from werkzeug import secure_filename
from werkzeug.security import gen_salt
from .models import User, Domain, Record, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord
from .models import User, Account, Domain, Record, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord
from app import app, login_manager, github, google
from app.lib import utils
from app.decorators import admin_role_required, can_access_domain, can_configure_dnssec
@ -465,7 +465,6 @@ def dashboard():
else:
logging.debug('Update domains in background')
# stats for dashboard
domain_count = Domain.query.count()
users = User.query.all()
@ -492,7 +491,7 @@ def dashboard_domains():
template = app.jinja_env.get_template("dashboard_domain.html")
render = template.make_module(vars={"current_user": current_user})
columns = [Domain.name, Domain.dnssec, Domain.type, Domain.serial, Domain.master]
columns = [Domain.name, Domain.dnssec, Domain.type, Domain.serial, Domain.master, Domain.account]
# History.created_on.desc()
order_by = []
for i in range(len(columns)):
@ -515,6 +514,12 @@ def dashboard_domains():
if search:
start = "" if search.startswith("^") else "%"
end = "" if search.endswith("$") else "%"
if current_user.role.name == 'Administrator':
domains = domains.outerjoin(Account).filter(Domain.name.ilike(start + search.strip("^$") + end) |
Account.name.ilike(start + search.strip("^$") + end) |
Account.description.ilike(start + search.strip("^$") + end))
else:
domains = domains.filter(Domain.name.ilike(start + search.strip("^$") + end))
filtered_count = domains.count()
@ -525,9 +530,6 @@ def dashboard_domains():
if length != -1:
domains = domains[start:start + length]
if current_user.role.name != 'Administrator':
domains = [d[2] for d in domains]
data = []
for domain in domains:
data.append([
@ -536,6 +538,7 @@ def dashboard_domains():
render.type(domain),
render.serial(domain),
render.master(domain),
render.account(domain),
render.actions(domain),
])
@ -612,6 +615,7 @@ def domain_add():
domain_type = request.form.getlist('radio_type')[0]
domain_template = request.form.getlist('domain_template')[0]
soa_edit_api = request.form.getlist('radio_type_soa_edit_api')[0]
account_id = request.form.getlist('accountid')[0]
if ' ' in domain_name or not domain_name or not domain_type:
return render_template('errors/400.html', msg="Please correct your input"), 400
@ -623,10 +627,13 @@ def domain_add():
domain_master_ips = domain_master_string.split(',')
else:
domain_master_ips = []
account_name = Account().get_name_by_id(account_id)
d = Domain()
result = d.add(domain_name=domain_name, domain_type=domain_type, soa_edit_api=soa_edit_api, domain_master_ips=domain_master_ips)
result = d.add(domain_name=domain_name, domain_type=domain_type, soa_edit_api=soa_edit_api, domain_master_ips=domain_master_ips, account_name=account_name)
if result['status'] == 'ok':
history = History(msg='Add domain {0}'.format(domain_name), detail=str({'domain_type': domain_type, 'domain_master_ips': domain_master_ips}), created_by=current_user.username)
history = History(msg='Add domain {0}'.format(domain_name), detail=str({'domain_type': domain_type, 'domain_master_ips': domain_master_ips, 'account_id': account_id}), created_by=current_user.username)
history.add()
if domain_template != '0':
template = DomainTemplate.query.filter(DomainTemplate.id == domain_template).first()
@ -649,7 +656,10 @@ def domain_add():
except:
logging.error(traceback.print_exc())
return redirect(url_for('error', code=500))
return render_template('domain_add.html', templates=templates)
else:
accounts = Account.query.all()
return render_template('domain_add.html', templates=templates, accounts=accounts)
@app.route('/admin/domain/<path:domain_name>/delete', methods=['GET'])
@ -677,12 +687,14 @@ def domain_management(domain_name):
if not domain:
return redirect(url_for('error', code=404))
users = User.query.all()
accounts = Account.query.all()
# get list of user ids to initilize selection data
d = Domain(name=domain_name)
domain_user_ids = d.get_user()
account = d.get_account()
return render_template('domain_management.html', domain=domain, users=users, domain_user_ids=domain_user_ids)
return render_template('domain_management.html', domain=domain, users=users, domain_user_ids=domain_user_ids, accounts=accounts, domain_account=account)
if request.method == 'POST':
# username in right column
@ -718,9 +730,32 @@ def domain_change_soa_edit_api(domain_name):
status = d.update_soa_setting(domain_name=domain_name, soa_edit_api=new_setting)
if status['status'] != None:
users = User.query.all()
accounts = Account.query.all()
d = Domain(name=domain_name)
domain_user_ids = d.get_user()
return render_template('domain_management.html', domain=domain, users=users, domain_user_ids=domain_user_ids, status=status)
account = d.get_account()
return render_template('domain_management.html', domain=domain, users=users, domain_user_ids=domain_user_ids, accounts=accounts, domain_account=account)
else:
return redirect(url_for('error', code=500))
@app.route('/admin/domain/<path:domain_name>/change_account', methods=['POST'])
@login_required
@admin_role_required
def domain_change_account(domain_name):
domain = Domain.query.filter(Domain.name == domain_name).first()
if not domain:
return redirect(url_for('error', code=404))
account_id = request.form.get('accountid')
status = domain.assoc_account(account_id)
if status['status']:
users = User.query.all()
accounts = Account.query.all()
d = Domain(name=domain_name)
domain_user_ids = d.get_user()
account = d.get_account()
return render_template('domain_management.html', domain=domain, users=users, domain_user_ids=domain_user_ids, accounts=accounts, domain_account=account)
else:
return redirect(url_for('error', code=500))
@ -1158,6 +1193,95 @@ def admin_manageuser():
return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400)
@app.route('/admin/account/edit/<account_name>', methods=['GET', 'POST'])
@app.route('/admin/account/edit', methods=['GET', 'POST'])
@login_required
@admin_role_required
def admin_editaccount(account_name=None):
users = User.query.all()
if request.method == 'GET':
if account_name is None:
return render_template('admin_editaccount.html', users=users, create=1)
else:
account = Account.query.filter(Account.name == account_name).first()
account_user_ids = account.get_user()
return render_template('admin_editaccount.html', account=account, account_user_ids=account_user_ids, users=users, create=0)
if request.method == 'POST':
fdata = request.form
new_user_list = request.form.getlist('account_multi_user')
# on POST, synthesize account and account_user_ids from form data
if not account_name:
account_name = fdata['accountname']
account = Account(name=account_name, description=fdata['accountdescription'], contact=fdata['accountcontact'], mail=fdata['accountmail'])
account_user_ids = []
for username in new_user_list:
userid = User(username=username).get_user_info_by_username().id
account_user_ids.append(userid)
create = int(fdata['create'])
if create:
# account __init__ sanitizes and lowercases the name, so to manage expectations
# we let the user reenter the name until it's not empty and it's valid (ignoring the case)
if account.name == "" or account.name != account_name.lower():
return render_template('admin_editaccount.html', account=account, account_user_ids=account_user_ids, users=users, create=create, invalid_accountname=True)
if Account.query.filter(Account.name == account.name).first():
return render_template('admin_editaccount.html', account=account, account_user_ids=account_user_ids, users=users, create=create, duplicate_accountname=True)
result = account.create_account()
history = History(msg='Create account {0}'.format(account.name), created_by=current_user.username)
else:
result = account.update_account()
history = History(msg='Update account {0}'.format(account.name), created_by=current_user.username)
if result['status']:
account.grant_privileges(new_user_list)
history.add()
return redirect(url_for('admin_manageaccount'))
return render_template('admin_editaccount.html', account=account, account_user_ids=account_user_ids, users=users, create=create, error=result['msg'])
@app.route('/admin/manageaccount', methods=['GET', 'POST'])
@login_required
@admin_role_required
def admin_manageaccount():
if request.method == 'GET':
accounts = Account.query.order_by(Account.name).all()
return render_template('admin_manageaccount.html', accounts=accounts)
if request.method == 'POST':
#
# post data should in format
# {'action': 'delete_account', 'data': 'accountname'}
#
try:
jdata = request.json
data = jdata['data']
if jdata['action'] == 'delete_account':
account = Account(name=data)
result = account.delete_account()
if result:
history = History(msg='Delete account {0}'.format(data), created_by=current_user.username)
history.add()
return make_response(jsonify( { 'status': 'ok', 'msg': 'Account has been removed.' } ), 200)
else:
return make_response(jsonify( { 'status': 'error', 'msg': 'Cannot remove account.' } ), 500)
else:
return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400)
except:
logging.error(traceback.print_exc())
return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400)
@app.route('/admin/history', methods=['GET', 'POST'])
@login_required
@admin_role_required