mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-11-08 22:50:26 +00:00
Merge and resolve the conflicts from master
This commit is contained in:
commit
8a22e030cd
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
app/static/* linguist-vendored
|
||||
|
@ -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)
|
||||
|
279
app/models.py
279
app/models.py
@ -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
|
||||
|
118
app/templates/admin_editaccount.html
Normal file
118
app/templates/admin_editaccount.html
Normal 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">×</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 %}
|
127
app/templates/admin_manageaccount.html
Normal file
127
app/templates/admin_manageaccount.html
Normal 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 <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 <i class="fa fa-cog"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-flat btn-danger button_delete" id="{{ account.name }}">
|
||||
Delete <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">×</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 %}
|
@ -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 %}
|
||||
|
@ -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') }}",
|
||||
|
@ -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%">
|
||||
|
@ -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">
|
||||
|
@ -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> Change account for {{ domain.name }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
|
146
app/views.py
146
app/views.py
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user