2016-06-16 08:33:05 +00:00
import os
2015-12-13 09:34:12 +00:00
import ldap
2017-09-03 18:23:18 +00:00
import ldap . filter
2015-12-13 09:34:12 +00:00
import time
2016-06-16 08:33:05 +00:00
import base64
2015-12-13 09:34:12 +00:00
import bcrypt
2016-02-11 09:54:15 +00:00
import itertools
2015-12-13 09:34:12 +00:00
import traceback
2016-09-17 13:37:20 +00:00
import pyotp
2016-11-16 13:09:13 +00:00
import re
2016-11-16 14:13:02 +00:00
import dns . reversename
2018-02-09 12:37:28 +00:00
import sys
2018-04-12 04:18:44 +00:00
import logging as logger
2015-12-13 09:34:12 +00:00
from datetime import datetime
2018-03-30 06:49:35 +00:00
from urllib . parse import urljoin
2016-11-21 12:44:47 +00:00
from distutils . util import strtobool
2016-06-07 08:20:56 +00:00
from distutils . version import StrictVersion
2016-07-01 19:41:41 +00:00
from flask_login import AnonymousUserMixin
2015-12-13 09:34:12 +00:00
from app import app , db
2018-03-30 06:49:35 +00:00
from app . lib import utils
2018-03-31 01:21:02 +00:00
2018-04-12 04:18:44 +00:00
logging = logger . getLogger ( __name__ )
2015-12-13 09:34:12 +00:00
2016-04-13 04:13:59 +00:00
if ' LDAP_TYPE ' in app . config . keys ( ) :
LDAP_URI = app . config [ ' LDAP_URI ' ]
2017-09-03 18:23:18 +00:00
if ' LDAP_USERNAME ' in app . config . keys ( ) and ' LDAP_PASSWORD ' in app . config . keys ( ) : #backward compatability
LDAP_BIND_TYPE = ' search '
if ' LDAP_BIND_TYPE ' in app . config . keys ( ) :
LDAP_BIND_TYPE = app . config [ ' LDAP_BIND_TYPE ' ]
if LDAP_BIND_TYPE == ' search ' :
LDAP_USERNAME = app . config [ ' LDAP_USERNAME ' ]
LDAP_PASSWORD = app . config [ ' LDAP_PASSWORD ' ]
2016-04-13 04:13:59 +00:00
LDAP_SEARCH_BASE = app . config [ ' LDAP_SEARCH_BASE ' ]
LDAP_TYPE = app . config [ ' LDAP_TYPE ' ]
2016-04-28 15:53:50 +00:00
LDAP_FILTER = app . config [ ' LDAP_FILTER ' ]
LDAP_USERNAMEFIELD = app . config [ ' LDAP_USERNAMEFIELD ' ]
2017-09-03 18:23:18 +00:00
2018-04-01 00:23:53 +00:00
LDAP_GROUP_SECURITY = app . config . get ( ' LDAP_GROUP_SECURITY ' )
if LDAP_GROUP_SECURITY == True :
2017-09-03 18:23:18 +00:00
LDAP_ADMIN_GROUP = app . config [ ' LDAP_ADMIN_GROUP ' ]
LDAP_USER_GROUP = app . config [ ' LDAP_USER_GROUP ' ]
2016-04-13 04:13:59 +00:00
else :
LDAP_TYPE = False
2015-12-13 09:34:12 +00:00
2016-08-25 03:00:47 +00:00
if ' PRETTY_IPV6_PTR ' in app . config . keys ( ) :
import dns . inet
import dns . name
PRETTY_IPV6_PTR = app . config [ ' PRETTY_IPV6_PTR ' ]
else :
PRETTY_IPV6_PTR = False
2015-12-13 09:34:12 +00:00
PDNS_STATS_URL = app . config [ ' PDNS_STATS_URL ' ]
PDNS_API_KEY = app . config [ ' PDNS_API_KEY ' ]
2016-06-07 06:50:31 +00:00
PDNS_VERSION = app . config [ ' PDNS_VERSION ' ]
API_EXTENDED_URL = utils . pdns_api_extended_uri ( PDNS_VERSION )
2015-12-13 09:34:12 +00:00
2016-06-07 08:20:56 +00:00
# Flag for pdns v4.x.x
# TODO: Find another way to do this
if StrictVersion ( PDNS_VERSION ) > = StrictVersion ( ' 4.0.0 ' ) :
NEW_SCHEMA = True
2016-06-26 13:53:29 +00:00
else :
NEW_SCHEMA = False
2015-12-13 09:34:12 +00:00
class Anonymous ( AnonymousUserMixin ) :
def __init__ ( self ) :
self . username = ' Anonymous '
class User ( db . Model ) :
id = db . Column ( db . Integer , primary_key = True )
username = db . Column ( db . String ( 64 ) , index = True , unique = True )
password = db . Column ( db . String ( 64 ) )
firstname = db . Column ( db . String ( 64 ) )
lastname = db . Column ( db . String ( 64 ) )
email = db . Column ( db . String ( 128 ) )
2015-12-16 17:50:28 +00:00
avatar = db . Column ( db . String ( 128 ) )
2016-06-16 08:33:05 +00:00
otp_secret = db . Column ( db . String ( 16 ) )
2015-12-13 09:34:12 +00:00
role_id = db . Column ( db . Integer , db . ForeignKey ( ' role.id ' ) )
2016-06-16 08:33:05 +00:00
def __init__ ( self , id = None , username = None , password = None , plain_text_password = None , firstname = None , lastname = None , role_id = None , email = None , avatar = None , otp_secret = None , reload_info = True ) :
2015-12-13 09:34:12 +00:00
self . id = id
self . username = username
self . password = password
self . plain_text_password = plain_text_password
self . firstname = firstname
self . lastname = lastname
self . role_id = role_id
self . email = email
2015-12-16 17:50:28 +00:00
self . avatar = avatar
2016-06-16 08:33:05 +00:00
self . otp_secret = otp_secret
2015-12-13 09:34:12 +00:00
2015-12-16 07:21:30 +00:00
if reload_info :
user_info = self . get_user_info_by_id ( ) if id else self . get_user_info_by_username ( )
2015-12-13 09:34:12 +00:00
2015-12-16 07:21:30 +00:00
if user_info :
self . id = user_info . id
self . username = user_info . username
self . firstname = user_info . firstname
self . lastname = user_info . lastname
self . email = user_info . email
self . role_id = user_info . role_id
2016-06-16 08:33:05 +00:00
self . otp_secret = user_info . otp_secret
2015-12-13 09:34:12 +00:00
def is_authenticated ( self ) :
return True
2016-08-23 02:52:35 +00:00
2015-12-13 09:34:12 +00:00
def is_active ( self ) :
return True
2016-08-23 02:52:35 +00:00
2015-12-13 09:34:12 +00:00
def is_anonymous ( self ) :
return False
def get_id ( self ) :
try :
return unicode ( self . id ) # python 2
except NameError :
return str ( self . id ) # python 3
def __repr__ ( self ) :
2018-04-01 00:57:41 +00:00
return ' <User {0} > ' . format ( self . username )
2015-12-13 09:34:12 +00:00
2016-06-16 08:33:05 +00:00
def get_totp_uri ( self ) :
2018-04-01 00:57:41 +00:00
return " otpauth://totp/PowerDNS-Admin: {0} ?secret= {1} &issuer=PowerDNS-Admin " . format ( self . username , self . otp_secret )
2016-06-16 08:33:05 +00:00
def verify_totp ( self , token ) :
2016-09-17 13:37:20 +00:00
totp = pyotp . TOTP ( self . otp_secret )
return totp . verify ( int ( token ) )
2016-06-16 08:33:05 +00:00
2015-12-13 09:34:12 +00:00
def get_hashed_password ( self , plain_text_password = None ) :
# Hash a password for the first time
# (Using bcrypt, the salt is saved into the hash itself)
2018-04-10 01:59:28 +00:00
if plain_text_password == None :
return plain_text_password
2015-12-13 09:34:12 +00:00
pw = plain_text_password if plain_text_password else self . plain_text_password
2016-09-17 14:25:05 +00:00
return bcrypt . hashpw ( pw . encode ( ' utf-8 ' ) , bcrypt . gensalt ( ) )
2015-12-13 09:34:12 +00:00
2016-08-23 02:52:35 +00:00
def check_password ( self , hashed_password ) :
2015-12-13 09:34:12 +00:00
# Check hased password. Useing bcrypt, the salt is saved into the hash itself
2017-11-06 22:36:11 +00:00
if ( self . plain_text_password ) :
return bcrypt . checkpw ( self . plain_text_password . encode ( ' utf-8 ' ) , hashed_password . encode ( ' utf-8 ' ) )
return False
2015-12-13 09:34:12 +00:00
def get_user_info_by_id ( self ) :
user_info = User . query . get ( int ( self . id ) )
return user_info
def get_user_info_by_username ( self ) :
user_info = User . query . filter ( User . username == self . username ) . first ( )
return user_info
def ldap_search ( self , searchFilter , baseDN ) :
searchScope = ldap . SCOPE_SUBTREE
retrieveAttributes = None
try :
ldap . set_option ( ldap . OPT_X_TLS_REQUIRE_CERT , ldap . OPT_X_TLS_NEVER )
l = ldap . initialize ( LDAP_URI )
2017-09-03 18:23:18 +00:00
l . set_option ( ldap . OPT_REFERRALS , ldap . OPT_OFF )
2015-12-13 09:34:12 +00:00
l . set_option ( ldap . OPT_PROTOCOL_VERSION , 3 )
l . set_option ( ldap . OPT_X_TLS , ldap . OPT_X_TLS_DEMAND )
l . set_option ( ldap . OPT_X_TLS_DEMAND , True )
l . set_option ( ldap . OPT_DEBUG_LEVEL , 255 )
l . protocol_version = ldap . VERSION3
2017-09-03 18:23:18 +00:00
if LDAP_BIND_TYPE == " direct " :
global LDAP_USERNAME ; LDAP_USERNAME = self . username
global LDAP_PASSWORD ; LDAP_PASSWORD = self . password
2015-12-13 09:34:12 +00:00
l . simple_bind_s ( LDAP_USERNAME , LDAP_PASSWORD )
ldap_result_id = l . search ( baseDN , searchScope , searchFilter , retrieveAttributes )
result_set = [ ]
while 1 :
result_type , result_data = l . result ( ldap_result_id , 0 )
if ( result_data == [ ] ) :
break
else :
if result_type == ldap . RES_SEARCH_ENTRY :
result_set . append ( result_data )
return result_set
2018-03-30 06:49:35 +00:00
except ldap . LDAPError as e :
2015-12-13 09:34:12 +00:00
logging . error ( e )
raise
def is_validate ( self , method ) :
"""
Validate user credential
"""
if method == ' LOCAL ' :
user_info = User . query . filter ( User . username == self . username ) . first ( )
if user_info :
2015-12-25 04:23:52 +00:00
if user_info . password and self . check_password ( user_info . password ) :
2018-04-01 00:23:53 +00:00
logging . info ( ' User " {0} " logged in successfully ' . format ( self . username ) )
2015-12-13 09:34:12 +00:00
return True
2018-04-01 00:23:53 +00:00
logging . error ( ' User " {0} " input a wrong password ' . format ( self . username ) )
2015-12-13 09:34:12 +00:00
return False
2018-04-01 00:23:53 +00:00
logging . warning ( ' User " {0} " does not exist ' . format ( self . username ) )
2016-08-23 04:10:00 +00:00
return False
if method == ' LDAP ' :
2017-09-03 18:23:18 +00:00
allowedlogin = False
isadmin = False
2016-04-13 04:13:59 +00:00
if not LDAP_TYPE :
logging . error ( ' LDAP authentication is disabled ' )
return False
2016-03-17 03:35:53 +00:00
if LDAP_TYPE == ' ldap ' :
2018-04-01 00:23:53 +00:00
searchFilter = " (&( {0} = {1} ) {2} ) " . format ( LDAP_USERNAMEFIELD , self . username , LDAP_FILTER )
logging . info ( ' Ldap searchFilter " {0} " ' . format ( searchFilter ) )
elif LDAP_TYPE == ' ad ' :
searchFilter = " (&(objectcategory=person)( {0} = {1} ) {2} ) " . format ( LDAP_USERNAMEFIELD , self . username , LDAP_FILTER )
2015-12-13 09:34:12 +00:00
2016-08-23 04:10:00 +00:00
result = self . ldap_search ( searchFilter , LDAP_SEARCH_BASE )
2015-12-13 09:34:12 +00:00
if not result :
2018-04-01 00:23:53 +00:00
logging . warning ( ' LDAP User " {0} " does not exist ' . format ( self . username ) )
2015-12-13 09:34:12 +00:00
return False
2016-08-23 04:10:00 +00:00
try :
2017-09-03 18:23:18 +00:00
ldap_username = ldap . filter . escape_filter_chars ( result [ 0 ] [ 0 ] [ 0 ] )
if LDAP_GROUP_SECURITY :
try :
if LDAP_TYPE == ' ldap ' :
ldap_user_dn = ldap . filter . escape_filter_chars ( result [ 0 ] [ 0 ] [ 0 ] )
logging . info ( result [ 0 ] [ 0 ] [ 0 ] )
2018-04-01 00:57:41 +00:00
if ( self . ldap_search ( ' (member= {0} ) ' . format ( ldap_user_dn ) , LDAP_ADMIN_GROUP ) ) :
2017-09-03 18:23:18 +00:00
allowedlogin = True
isadmin = True
2018-04-01 00:23:53 +00:00
logging . info ( ' User {0} is part of the " {1} " group that allows admin access to PowerDNS-Admin ' . format ( self . username , LDAP_ADMIN_GROUP ) )
2018-04-01 00:57:41 +00:00
if ( self . ldap_search ( ' (member= {0} ) ' . format ( ldap_user_dn ) , LDAP_USER_GROUP ) ) :
2017-09-03 18:23:18 +00:00
#if (group == LDAP_USER_GROUP):
allowedlogin = True
2018-04-01 00:23:53 +00:00
logging . info ( ' User {0} is part of the " {1} " group that allows user access to PowerDNS-Admin ' . format ( self . username , LDAP_USER_GROUP ) )
2017-09-03 18:23:18 +00:00
if allowedlogin == False :
2018-04-01 00:23:53 +00:00
logging . error ( ' User {0} is not part of the " {1} " or " {2} " groups that allow access to PowerDNS-Admin ' . format ( self . username , LDAP_ADMIN_GROUP , LDAP_USER_GROUP ) )
2017-09-03 18:23:18 +00:00
return False
2018-04-01 00:23:53 +00:00
except Exception as e :
logging . error ( ' LDAP group lookup for user " {0} " has failed ' . format ( e ) )
2017-09-03 18:23:18 +00:00
return False
2018-04-01 00:23:53 +00:00
logging . info ( ' User " {0} " logged in successfully ' . format ( self . username ) )
except Exception as e :
logging . error ( ' User " {0} " input a wrong LDAP password ' . format ( e ) )
2016-08-23 04:10:00 +00:00
return False
# create user if not exist in the db
if not User . query . filter ( User . username == self . username ) . first ( ) :
2017-09-03 18:23:18 +00:00
self . firstname = self . username
self . lastname = ' '
2015-12-13 09:34:12 +00:00
try :
2016-08-23 04:10:00 +00:00
# try to get user's firstname & lastname from LDAP
# this might be changed in the future
2017-09-03 18:23:18 +00:00
self . firstname = result [ 0 ] [ 0 ] [ 1 ] [ ' givenName ' ]
self . lastname = result [ 0 ] [ 0 ] [ 1 ] [ ' sn ' ]
self . email = result [ 0 ] [ 0 ] [ 1 ] [ ' mail ' ]
2018-04-01 00:23:53 +00:00
except Exception as e :
logging . info ( " reading ldap data threw an exception {0} " . format ( e ) )
2016-08-23 04:10:00 +00:00
# first register user will be in Administrator role
self . role_id = Role . query . filter_by ( name = ' User ' ) . first ( ) . id
if User . query . count ( ) == 0 :
self . role_id = Role . query . filter_by ( name = ' Administrator ' ) . first ( ) . id
2016-08-23 02:52:35 +00:00
2017-09-03 18:23:18 +00:00
# user will be in Administrator role if part of LDAP Admin group
if LDAP_GROUP_SECURITY :
if isadmin == True :
self . role_id = Role . query . filter_by ( name = ' Administrator ' ) . first ( ) . id
2016-08-23 04:10:00 +00:00
self . create_user ( )
2018-04-01 00:23:53 +00:00
logging . info ( ' Created user " {0} " in the DB ' . format ( self . username ) )
2016-08-23 04:10:00 +00:00
2017-09-03 18:23:18 +00:00
# user already exists in database, set their admin status based on group membership (if enabled)
if LDAP_GROUP_SECURITY :
self . set_admin ( isadmin )
self . update_profile ( )
2016-08-23 04:10:00 +00:00
return True
2015-12-13 09:34:12 +00:00
else :
logging . error ( ' Unsupported authentication method ' )
return False
def create_user ( self ) :
"""
If user logged in successfully via LDAP in the first time
We will create a local user ( in DB ) in order to manage user
profile such as name , roles , . . .
"""
2018-03-01 07:26:29 +00:00
2016-09-28 08:50:37 +00:00
# Set an invalid password hash for non local users
self . password = ' * '
2018-03-01 07:26:29 +00:00
2016-08-23 04:10:00 +00:00
db . session . add ( self )
2015-12-13 09:34:12 +00:00
db . session . commit ( )
def create_local_user ( self ) :
"""
Create local user witch stores username / password in the DB
"""
# check if username existed
user = User . query . filter ( User . username == self . username ) . first ( )
if user :
2018-03-30 10:43:34 +00:00
return { ' status ' : False , ' msg ' : ' Username is already in use ' }
2015-12-13 09:34:12 +00:00
# check if email existed
user = User . query . filter ( User . email == self . email ) . first ( )
if user :
2018-03-30 10:43:34 +00:00
return { ' status ' : False , ' msg ' : ' Email address is already in use ' }
2015-12-13 09:34:12 +00:00
2016-08-23 04:10:00 +00:00
# first register user will be in Administrator role
self . role_id = Role . query . filter_by ( name = ' User ' ) . first ( ) . id
if User . query . count ( ) == 0 :
self . role_id = Role . query . filter_by ( name = ' Administrator ' ) . first ( ) . id
2018-03-30 06:49:35 +00:00
2016-08-23 04:10:00 +00:00
self . password = self . get_hashed_password ( self . plain_text_password )
2018-04-10 01:59:28 +00:00
if self . password :
self . password = self . password . decode ( " utf-8 " )
2015-12-17 15:35:04 +00:00
2016-08-23 04:10:00 +00:00
db . session . add ( self )
db . session . commit ( )
2018-03-30 10:43:34 +00:00
return { ' status ' : True , ' msg ' : ' Created user successfully ' }
2015-12-13 09:34:12 +00:00
2016-06-16 08:33:05 +00:00
def update_profile ( self , enable_otp = None ) :
2015-12-16 07:21:30 +00:00
"""
Update user profile
"""
2016-08-23 04:10:00 +00:00
2015-12-16 07:21:30 +00:00
user = User . query . filter ( User . username == self . username ) . first ( )
2016-08-23 04:10:00 +00:00
if not user :
return False
2016-06-16 08:33:05 +00:00
2016-08-23 04:10:00 +00:00
user . firstname = self . firstname if self . firstname else user . firstname
user . lastname = self . lastname if self . lastname else user . lastname
user . email = self . email if self . email else user . email
user . password = self . get_hashed_password ( self . plain_text_password ) if self . plain_text_password else user . password
user . avatar = self . avatar if self . avatar else user . avatar
2018-04-10 01:59:28 +00:00
if enable_otp is not None :
user . otp_secret = " "
2016-08-23 04:10:00 +00:00
if enable_otp == True :
# generate the opt secret key
user . otp_secret = base64 . b32encode ( os . urandom ( 10 ) ) . decode ( ' utf-8 ' )
try :
db . session . add ( user )
db . session . commit ( )
return True
except Exception :
db . session . rollback ( )
return False
2015-12-16 07:21:30 +00:00
2017-09-15 13:14:04 +00:00
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 )
2015-12-13 09:34:12 +00:00
def get_domain ( self ) :
"""
Get domains which user has permission to
access
"""
2017-09-15 13:14:04 +00:00
return [ q [ 2 ] for q in self . get_domain_query ( ) ]
2015-12-13 09:34:12 +00:00
def delete ( self ) :
"""
Delete a user
"""
# revoke all user privileges first
self . revoke_privilege ( )
try :
User . query . filter ( User . username == self . username ) . delete ( )
db . session . commit ( )
return True
except :
db . session . rollback ( )
2018-04-01 00:57:41 +00:00
logging . error ( ' Cannot delete user {0} from DB ' . format ( self . username ) )
2015-12-13 09:34:12 +00:00
return False
def revoke_privilege ( self ) :
"""
Revoke all privielges from a user
"""
user = User . query . filter ( User . username == self . username ) . first ( )
2016-08-23 02:52:35 +00:00
2015-12-13 09:34:12 +00:00
if user :
user_id = user . id
try :
DomainUser . query . filter ( DomainUser . user_id == user_id ) . delete ( )
db . session . commit ( )
return True
except :
db . session . rollback ( )
2018-04-01 00:57:41 +00:00
logging . error ( ' Cannot revoke user {0} privielges ' . format ( self . username ) )
2015-12-13 09:34:12 +00:00
return False
return False
def set_admin ( self , is_admin ) :
"""
Set role for a user :
is_admin == True = > Administrator
is_admin == False = > User
"""
user_role_name = ' Administrator ' if is_admin else ' User '
role = Role . query . filter ( Role . name == user_role_name ) . first ( )
try :
if role :
user = User . query . filter ( User . username == self . username ) . first ( )
user . role_id = role . id
db . session . commit ( )
return True
else :
return False
except :
db . session . roleback ( )
logging . error ( ' Cannot change user role in DB ' )
logging . debug ( traceback . format_exc ( ) )
return False
class Role ( db . Model ) :
id = db . Column ( db . Integer , primary_key = True )
name = db . Column ( db . String ( 64 ) , index = True , unique = True )
description = db . Column ( db . String ( 128 ) )
users = db . relationship ( ' User ' , backref = ' role ' , lazy = ' dynamic ' )
2016-04-11 09:40:44 +00:00
def __init__ ( self , id = None , name = None , description = None ) :
2015-12-13 09:34:12 +00:00
self . id = id
self . name = name
self . description = description
2016-08-23 02:52:35 +00:00
# allow database autoincrement to do its own ID assignments
2016-04-11 09:40:44 +00:00
def __init__ ( self , name = None , description = None ) :
self . id = None
self . name = name
self . description = description
2015-12-13 09:34:12 +00:00
def __repr__ ( self ) :
2018-04-01 00:57:41 +00:00
return ' <Role {0} r> ' . format ( self . name )
2015-12-13 09:34:12 +00:00
2016-07-02 17:24:13 +00:00
class DomainSetting ( db . Model ) :
__tablename__ = ' domain_setting '
id = db . Column ( db . Integer , primary_key = True )
domain_id = db . Column ( db . Integer , db . ForeignKey ( ' domain.id ' ) )
domain = db . relationship ( ' Domain ' , back_populates = ' settings ' )
setting = db . Column ( db . String ( 255 ) , nullable = False )
value = db . Column ( db . String ( 255 ) )
2016-08-23 02:52:35 +00:00
2016-07-02 17:24:13 +00:00
def __init__ ( self , id = None , setting = None , value = None ) :
self . id = id
self . setting = setting
self . value = value
2016-08-23 02:52:35 +00:00
2016-07-02 17:24:13 +00:00
def __repr__ ( self ) :
2018-04-01 00:57:41 +00:00
return ' <DomainSetting {0} for {1} > ' . format ( setting , self . domain . name )
2016-08-23 02:52:35 +00:00
2016-07-02 17:24:13 +00:00
def __eq__ ( self , other ) :
return self . setting == other . setting
2016-08-23 02:52:35 +00:00
2016-07-02 17:24:13 +00:00
def set ( self , value ) :
try :
self . value = value
db . session . commit ( )
return True
except :
logging . error ( ' Unable to set DomainSetting value ' )
logging . debug ( traceback . format_exc ( ) )
db . session . rollback ( )
return False
2015-12-13 09:34:12 +00:00
class Domain ( db . Model ) :
id = db . Column ( db . Integer , primary_key = True )
name = db . Column ( db . String ( 255 ) , index = True , unique = True )
master = db . Column ( db . String ( 128 ) )
type = db . Column ( db . String ( 6 ) , nullable = False )
serial = db . Column ( db . Integer )
notified_serial = db . Column ( db . Integer )
last_check = db . Column ( db . Integer )
2016-03-24 13:01:08 +00:00
dnssec = db . Column ( db . Integer )
2016-07-02 17:24:13 +00:00
settings = db . relationship ( ' DomainSetting ' , back_populates = ' domain ' )
2015-12-13 09:34:12 +00:00
2016-04-14 05:19:02 +00:00
def __init__ ( self , id = None , name = None , master = None , type = ' NATIVE ' , serial = None , notified_serial = None , last_check = None , dnssec = None ) :
2015-12-13 09:34:12 +00:00
self . id = id
self . name = name
self . master = master
self . type = type
self . serial = serial
self . notified_serial = notified_serial
self . last_check = last_check
2016-03-24 13:01:08 +00:00
self . dnssec = dnssec
2015-12-13 09:34:12 +00:00
def __repr__ ( self ) :
2018-04-01 00:57:41 +00:00
return ' <Domain {0} > ' . format ( self . name )
2016-08-23 02:52:35 +00:00
2016-07-02 17:24:13 +00:00
def add_setting ( self , setting , value ) :
try :
self . settings . append ( DomainSetting ( setting = setting , value = value ) )
db . session . commit ( )
return True
2018-03-30 06:49:35 +00:00
except Exception as e :
2018-04-01 00:57:41 +00:00
logging . error ( ' Can not create setting {0} for domain {1} . {2} ' . format ( setting , self . name , e ) )
2016-07-02 17:24:13 +00:00
return False
2015-12-13 09:34:12 +00:00
2018-04-12 04:18:44 +00:00
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
2015-12-13 09:34:12 +00:00
def get_domains ( self ) :
"""
Get all domains which has in PowerDNS
"""
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
2018-03-30 06:49:35 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones ' ) , headers = headers )
2015-12-13 09:34:12 +00:00
return jdata
def get_id_by_name ( self , name ) :
"""
Return domain id
"""
2016-11-16 13:02:43 +00:00
try :
domain = Domain . query . filter ( Domain . name == name ) . first ( )
return domain . id
except :
return None
2015-12-13 09:34:12 +00:00
def update ( self ) :
"""
Fetch zones ( domains ) from PowerDNS and update into DB
"""
db_domain = Domain . query . all ( )
list_db_domain = [ d . name for d in db_domain ]
2016-06-16 03:31:36 +00:00
dict_db_domain = dict ( ( x . name , x ) for x in db_domain )
2015-12-13 09:34:12 +00:00
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
try :
2018-03-30 06:49:35 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones ' ) , headers = headers )
2016-06-07 08:20:56 +00:00
list_jdomain = [ d [ ' name ' ] . rstrip ( ' . ' ) for d in jdata ]
2015-12-13 09:34:12 +00:00
try :
# domains should remove from db since it doesn't exist in powerdns anymore
should_removed_db_domain = list ( set ( list_db_domain ) . difference ( list_jdomain ) )
for d in should_removed_db_domain :
# revoke permission before delete domain
domain = Domain . query . filter ( Domain . name == d ) . first ( )
domain_user = DomainUser . query . filter ( DomainUser . domain_id == domain . id )
if domain_user :
domain_user . delete ( )
db . session . commit ( )
2016-11-21 12:30:16 +00:00
domain_setting = DomainSetting . query . filter ( DomainSetting . domain_id == domain . id )
if domain_setting :
domain_setting . delete ( )
db . session . commit ( )
2015-12-13 09:34:12 +00:00
# then remove domain
Domain . query . filter ( Domain . name == d ) . delete ( )
db . session . commit ( )
except :
logging . error ( ' Can not delete domain from DB ' )
logging . debug ( traceback . format_exc ( ) )
db . session . rollback ( )
# update/add new domain
for data in jdata :
2016-06-16 03:31:36 +00:00
d = dict_db_domain . get ( data [ ' name ' ] . rstrip ( ' . ' ) , None )
changed = False
2015-12-13 09:34:12 +00:00
if d :
2016-06-16 03:31:36 +00:00
# existing domain, only update if something actually has changed
if ( d . master != str ( data [ ' masters ' ] )
or d . type != data [ ' kind ' ]
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 ' ] ) :
d . master = str ( data [ ' masters ' ] )
d . type = data [ ' kind ' ]
d . serial = data [ ' serial ' ]
d . notified_serial = data [ ' notified_serial ' ]
d . last_check = 1 if data [ ' last_check ' ] else 0
2016-07-04 15:12:24 +00:00
d . dnssec = 1 if data [ ' dnssec ' ] else 0
2016-06-16 03:31:36 +00:00
changed = True
2015-12-13 09:34:12 +00:00
else :
# add new domain
d = Domain ( )
2016-06-07 08:20:56 +00:00
d . name = data [ ' name ' ] . rstrip ( ' . ' )
2015-12-13 09:34:12 +00:00
d . master = str ( data [ ' masters ' ] )
d . type = data [ ' kind ' ]
d . serial = data [ ' serial ' ]
d . notified_serial = data [ ' notified_serial ' ]
d . last_check = data [ ' last_check ' ]
2016-04-14 05:19:02 +00:00
d . dnssec = 1 if data [ ' dnssec ' ] else 0
2015-12-13 09:34:12 +00:00
db . session . add ( d )
2016-06-16 03:31:36 +00:00
changed = True
if changed :
try :
db . session . commit ( )
except :
db . session . rollback ( )
2015-12-13 09:34:12 +00:00
return { ' status ' : ' ok ' , ' msg ' : ' Domain table has been updated successfully ' }
2018-03-30 06:49:35 +00:00
except Exception as e :
logging . error ( ' Can not update domain table. Error: {0} ' . format ( e ) )
2015-12-13 09:34:12 +00:00
return { ' status ' : ' error ' , ' msg ' : ' Can not update domain table ' }
2016-03-05 10:04:12 +00:00
def add ( self , domain_name , domain_type , soa_edit_api , domain_ns = [ ] , domain_master_ips = [ ] ) :
2015-12-13 09:34:12 +00:00
"""
Add a domain to power dns
"""
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
2016-03-05 10:04:12 +00:00
2016-06-07 08:20:56 +00:00
if NEW_SCHEMA :
domain_name = domain_name + ' . '
domain_ns = [ ns + ' . ' for ns in domain_ns ]
2018-05-24 18:12:12 +00:00
if soa_edit_api not in [ " DEFAULT " , " INCREASE " , " EPOCH " , " OFF " ] :
soa_edit_api = ' DEFAULT '
elif soa_edit_api == ' OFF ' :
soa_edit_api = ' '
post_data = {
" name " : domain_name ,
" kind " : domain_type ,
" masters " : domain_master_ips ,
" nameservers " : domain_ns ,
" soa_edit_api " : soa_edit_api
}
2016-03-05 10:04:12 +00:00
2015-12-13 09:34:12 +00:00
try :
2018-03-30 06:49:35 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones ' ) , headers = headers , method = ' POST ' , data = post_data )
2015-12-13 09:34:12 +00:00
if ' error ' in jdata . keys ( ) :
logging . error ( jdata [ ' error ' ] )
return { ' status ' : ' error ' , ' msg ' : jdata [ ' error ' ] }
else :
2018-04-01 00:57:41 +00:00
logging . info ( ' Added domain {0} successfully ' . format ( domain_name ) )
2015-12-13 09:34:12 +00:00
return { ' status ' : ' ok ' , ' msg ' : ' Added domain successfully ' }
2018-03-30 06:49:35 +00:00
except Exception as e :
2018-04-01 00:57:41 +00:00
logging . error ( ' Cannot add domain {0} ' . format ( domain_name ) )
logging . debug ( traceback . print_exc ( ) )
2015-12-13 09:34:12 +00:00
return { ' status ' : ' error ' , ' msg ' : ' Cannot add this domain. ' }
2018-03-27 23:41:33 +00:00
def update_soa_setting ( self , domain_name , soa_edit_api ) :
domain = Domain . query . filter ( Domain . name == domain_name ) . first ( )
if not domain :
return { ' status ' : ' error ' , ' msg ' : ' Domain doesnt exist. ' }
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
2018-05-24 18:12:12 +00:00
if soa_edit_api not in [ " DEFAULT " , " INCREASE " , " EPOCH " , " OFF " ] :
soa_edit_api = ' DEFAULT '
elif soa_edit_api == ' OFF ' :
soa_edit_api = ' '
post_data = {
" soa_edit_api " : soa_edit_api ,
" kind " : domain . type
}
2018-03-27 23:41:33 +00:00
try :
jdata = utils . fetch_json (
2018-04-02 06:38:53 +00:00
urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} ' . format ( domain . name ) ) , headers = headers ,
2018-03-27 23:41:33 +00:00
method = ' PUT ' , data = post_data )
if ' error ' in jdata . keys ( ) :
logging . error ( jdata [ ' error ' ] )
return { ' status ' : ' error ' , ' msg ' : jdata [ ' error ' ] }
else :
2018-04-02 06:38:53 +00:00
logging . info ( ' soa-edit-api changed for domain {0} successfully ' . format ( domain_name ) )
2018-03-27 23:41:33 +00:00
return { ' status ' : ' ok ' , ' msg ' : ' soa-edit-api changed successfully ' }
2018-04-02 06:38:53 +00:00
except Exception as e :
logging . debug ( e )
logging . debug ( traceback . format_exc ( ) )
logging . error ( ' Cannot change soa-edit-api for domain {0} ' . format ( domain_name ) )
2018-03-27 23:41:33 +00:00
return { ' status ' : ' error ' , ' msg ' : ' Cannot change soa-edit-api this domain. ' }
2016-11-16 13:09:13 +00:00
def create_reverse_domain ( self , domain_name , domain_reverse_name ) :
"""
2018-03-01 07:26:29 +00:00
Check the existing reverse lookup domain ,
2016-11-16 13:09:13 +00:00
if not exists create a new one automatically
"""
2016-11-21 18:36:43 +00:00
domain_obj = Domain . query . filter ( Domain . name == domain_name ) . first ( )
2016-11-21 12:44:47 +00:00
domain_auto_ptr = DomainSetting . query . filter ( DomainSetting . domain == domain_obj ) . filter ( DomainSetting . setting == ' auto_ptr ' ) . first ( )
domain_auto_ptr = strtobool ( domain_auto_ptr . value ) if domain_auto_ptr else False
system_auto_ptr = Setting . query . filter ( Setting . name == ' auto_ptr ' ) . first ( )
system_auto_ptr = strtobool ( system_auto_ptr . value )
2016-11-16 13:09:13 +00:00
self . name = domain_name
domain_id = self . get_id_by_name ( domain_reverse_name )
2016-11-21 12:44:47 +00:00
if None == domain_id and \
(
system_auto_ptr or \
domain_auto_ptr
) :
2018-05-24 18:12:12 +00:00
result = self . add ( domain_reverse_name , ' Master ' , ' DEFAULT ' , ' ' , ' ' )
2016-11-16 13:09:13 +00:00
self . update ( )
if result [ ' status ' ] == ' ok ' :
2018-04-01 00:57:41 +00:00
history = History ( msg = ' Add reverse lookup domain {0} ' . format ( domain_reverse_name ) , detail = str ( { ' domain_type ' : ' Master ' , ' domain_master_ips ' : ' ' } ) , created_by = ' System ' )
2016-11-16 13:09:13 +00:00
history . add ( )
else :
return { ' status ' : ' error ' , ' msg ' : ' Adding reverse lookup domain failed ' }
domain_user_ids = self . get_user ( )
domain_users = [ ]
u = User ( )
for uid in domain_user_ids :
u . id = uid
tmp = u . get_user_info_by_id ( )
domain_users . append ( tmp . username )
if 0 != len ( domain_users ) :
self . name = domain_reverse_name
self . grant_privielges ( domain_users )
return { ' status ' : ' ok ' , ' msg ' : ' New reverse lookup domain created with granted privilages ' }
return { ' status ' : ' ok ' , ' msg ' : ' New reverse lookup domain created without users ' }
return { ' status ' : ' ok ' , ' msg ' : ' Reverse lookup domain already exists ' }
2015-12-13 09:34:12 +00:00
2016-11-21 18:40:43 +00:00
def get_reverse_domain_name ( self , reverse_host_address ) :
2016-11-28 07:39:07 +00:00
c = 1
2016-11-21 18:40:43 +00:00
if re . search ( ' ip6.arpa ' , reverse_host_address ) :
2016-11-28 07:39:07 +00:00
for i in range ( 1 , 32 , 1 ) :
2016-11-21 18:40:43 +00:00
address = re . search ( ' ((([a-f0-9] \ .) { ' + str ( i ) + ' })(?P<ipname>.+6.arpa) \ .?) ' , reverse_host_address )
if None != self . get_id_by_name ( address . group ( ' ipname ' ) ) :
2016-11-28 07:39:07 +00:00
c = i
2016-11-21 18:40:43 +00:00
break
2016-11-28 07:39:07 +00:00
return re . search ( ' ((([a-f0-9] \ .) { ' + str ( c ) + ' })(?P<ipname>.+6.arpa) \ .?) ' , reverse_host_address ) . group ( ' ipname ' )
2016-11-21 18:40:43 +00:00
else :
2016-11-28 07:39:07 +00:00
for i in range ( 1 , 4 , 1 ) :
2016-11-21 18:40:43 +00:00
address = re . search ( ' ((([0-9]+ \ .) { ' + str ( i ) + ' })(?P<ipname>.+r.arpa) \ .?) ' , reverse_host_address )
if None != self . get_id_by_name ( address . group ( ' ipname ' ) ) :
2016-11-28 07:39:07 +00:00
c = i
2016-11-21 18:40:43 +00:00
break
2016-11-28 07:39:07 +00:00
return re . search ( ' ((([0-9]+ \ .) { ' + str ( c ) + ' })(?P<ipname>.+r.arpa) \ .?) ' , reverse_host_address ) . group ( ' ipname ' )
2015-12-13 09:34:12 +00:00
def delete ( self , domain_name ) :
"""
Delete a single domain name from powerdns
"""
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
try :
2018-04-01 00:57:41 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} ' . format ( domain_name ) ) , headers = headers , method = ' DELETE ' )
logging . info ( ' Delete domain {0} successfully ' . format ( domain_name ) )
2015-12-13 09:34:12 +00:00
return { ' status ' : ' ok ' , ' msg ' : ' Delete domain successfully ' }
2018-03-30 06:49:35 +00:00
except Exception as e :
2018-04-01 00:57:41 +00:00
logging . error ( ' Cannot delete domain {0} ' . format ( domain_name ) )
logging . debug ( traceback . print_exc ( ) )
2015-12-13 09:34:12 +00:00
return { ' status ' : ' error ' , ' msg ' : ' Cannot delete domain ' }
def get_user ( self ) :
"""
Get users ( id ) who have access to this domain name
"""
user_ids = [ ]
query = db . session . query ( DomainUser , Domain ) . filter ( User . id == DomainUser . user_id ) . filter ( Domain . id == DomainUser . domain_id ) . filter ( Domain . name == self . name ) . all ( )
for q in query :
user_ids . append ( q [ 0 ] . user_id )
return user_ids
def grant_privielges ( self , new_user_list ) :
"""
Reconfigure domain_user table
"""
domain_id = self . get_id_by_name ( self . name )
2016-08-23 02:52:35 +00:00
2015-12-13 09:34:12 +00:00
domain_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 [ ]
2016-08-23 02:52:35 +00:00
2015-12-13 09:34:12 +00:00
removed_ids = list ( set ( domain_user_ids ) . difference ( new_user_ids ) )
added_ids = list ( set ( new_user_ids ) . difference ( domain_user_ids ) )
try :
for uid in removed_ids :
DomainUser . query . filter ( DomainUser . user_id == uid ) . filter ( DomainUser . domain_id == domain_id ) . delete ( )
db . session . commit ( )
except :
db . session . rollback ( )
2018-04-01 00:57:41 +00:00
logging . error ( ' Cannot revoke user privielges on domain {0} ' . format ( self . name ) )
2015-12-13 09:34:12 +00:00
try :
for uid in added_ids :
du = DomainUser ( domain_id , uid )
db . session . add ( du )
db . session . commit ( )
except :
db . session . rollback ( )
2018-04-01 00:57:41 +00:00
logging . error ( ' Cannot grant user privielges to domain {0} ' . format ( self . name ) )
2015-12-13 09:34:12 +00:00
def update_from_master ( self , domain_name ) :
"""
Update records from Master DNS server
"""
domain = Domain . query . filter ( Domain . name == domain_name ) . first ( )
if domain :
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
try :
2018-05-27 15:28:40 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} /axfr-retrieve ' . format ( domain . name ) ) , headers = headers , method = ' PUT ' )
2015-12-13 09:34:12 +00:00
return { ' status ' : ' ok ' , ' msg ' : ' Update from Master successfully ' }
except :
return { ' status ' : ' error ' , ' msg ' : ' There was something wrong, please contact administrator ' }
else :
return { ' status ' : ' error ' , ' msg ' : ' This domain doesnot exist ' }
2016-03-24 13:01:08 +00:00
def get_domain_dnssec ( self , domain_name ) :
"""
Get domain DNSSEC information
"""
domain = Domain . query . filter ( Domain . name == domain_name ) . first ( )
if domain :
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
try :
2018-04-01 00:57:41 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} /cryptokeys ' . format ( domain . name ) ) , headers = headers , method = ' GET ' )
2016-03-24 13:01:08 +00:00
if ' error ' in jdata :
return { ' status ' : ' error ' , ' msg ' : ' DNSSEC is not enabled for this domain ' }
else :
return { ' status ' : ' ok ' , ' dnssec ' : jdata }
except :
return { ' status ' : ' error ' , ' msg ' : ' There was something wrong, please contact administrator ' }
else :
return { ' status ' : ' error ' , ' msg ' : ' This domain doesnot exist ' }
2018-03-01 07:26:29 +00:00
def enable_domain_dnssec ( self , domain_name ) :
"""
Enable domain DNSSEC
"""
domain = Domain . query . filter ( Domain . name == domain_name ) . first ( )
if domain :
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
post_data = {
" keytype " : " ksk " ,
" active " : True
}
try :
2018-04-02 06:38:53 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} /cryptokeys ' . format ( domain . name ) ) , headers = headers , method = ' POST ' , data = post_data )
2018-03-01 07:26:29 +00:00
if ' error ' in jdata :
2018-06-07 02:28:14 +00:00
return { ' status ' : ' error ' , ' msg ' : ' Cannot enable DNSSEC for this domain. Error: {0} ' . format ( jdata [ ' error ' ] ) , ' jdata ' : jdata }
2018-03-01 07:26:29 +00:00
else :
return { ' status ' : ' ok ' }
except :
2018-04-02 06:38:53 +00:00
logging . error ( traceback . print_exc ( ) )
2018-03-01 07:26:29 +00:00
return { ' status ' : ' error ' , ' msg ' : ' There was something wrong, please contact administrator ' }
else :
2018-04-02 06:38:53 +00:00
return { ' status ' : ' error ' , ' msg ' : ' This domain does not exist ' }
2016-03-24 13:01:08 +00:00
2018-03-05 14:06:40 +00:00
def delete_dnssec_key ( self , domain_name , key_id ) :
"""
Remove keys DNSSEC
"""
domain = Domain . query . filter ( Domain . name == domain_name ) . first ( )
if domain :
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
2018-04-02 06:38:53 +00:00
url = ' /servers/localhost/zones/ {0} /cryptokeys/ {1} ' . format ( domain . name , key_id )
2018-03-05 14:06:40 +00:00
try :
2018-04-02 06:38:53 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + url ) , headers = headers , method = ' DELETE ' )
2018-03-05 14:06:40 +00:00
if ' error ' in jdata :
2018-06-07 02:28:14 +00:00
return { ' status ' : ' error ' , ' msg ' : ' Cannot disable DNSSEC for this domain. Error: {0} ' . format ( jdata [ ' error ' ] ) , ' jdata ' : jdata }
2018-03-05 14:06:40 +00:00
else :
return { ' status ' : ' ok ' }
except :
return { ' status ' : ' error ' , ' msg ' : ' There was something wrong, please contact administrator ' , ' id ' : key_id , ' url ' : url }
else :
return { ' status ' : ' error ' , ' msg ' : ' This domain doesnot exist ' }
2016-03-24 13:01:08 +00:00
2018-04-12 04:18:44 +00:00
2015-12-13 09:34:12 +00:00
class DomainUser ( db . Model ) :
__tablename__ = ' domain_user '
id = db . Column ( db . Integer , primary_key = True )
domain_id = db . Column ( db . Integer , db . ForeignKey ( ' domain.id ' ) , nullable = False )
user_id = db . Column ( db . Integer , db . ForeignKey ( ' user.id ' ) , nullable = False )
def __init__ ( self , domain_id , user_id ) :
self . domain_id = domain_id
self . user_id = user_id
def __repr__ ( self ) :
2018-04-01 00:57:41 +00:00
return ' <Domain_User {0} {1} > ' . format ( self . domain_id , self . user_id )
2015-12-13 09:34:12 +00:00
class Record ( object ) :
"""
This is not a model , it ' s just an object
which be assigned data from PowerDNS API
"""
def __init__ ( self , name = None , type = None , status = None , ttl = None , data = None ) :
self . name = name
self . type = type
self . status = status
self . ttl = ttl
self . data = data
def get_record_data ( self , domain ) :
"""
Query domain ' s DNS records via API
"""
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
try :
2018-04-01 00:57:41 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} ' . format ( domain ) ) , headers = headers )
2015-12-13 09:34:12 +00:00
except :
logging . error ( " Cannot fetch domain ' s record data from remote powerdns api " )
return False
2016-06-07 08:20:56 +00:00
if NEW_SCHEMA :
rrsets = jdata [ ' rrsets ' ]
for rrset in rrsets :
2016-08-19 23:04:20 +00:00
r_name = rrset [ ' name ' ] . rstrip ( ' . ' )
if PRETTY_IPV6_PTR : # only if activated
if rrset [ ' type ' ] == ' PTR ' : # only ptr
if ' ip6.arpa ' in r_name : # only if v6-ptr
2016-08-19 23:07:36 +00:00
r_name = dns . reversename . to_address ( dns . name . from_text ( r_name ) )
2016-08-19 23:04:20 +00:00
rrset [ ' name ' ] = r_name
2016-06-07 08:20:56 +00:00
rrset [ ' content ' ] = rrset [ ' records ' ] [ 0 ] [ ' content ' ]
rrset [ ' disabled ' ] = rrset [ ' records ' ] [ 0 ] [ ' disabled ' ]
return { ' records ' : rrsets }
2015-12-13 09:34:12 +00:00
return jdata
def add ( self , domain ) :
"""
Add a record to domain
"""
# validate record first
r = self . get_record_data ( domain )
records = r [ ' records ' ]
2018-04-18 06:29:29 +00:00
check = list ( filter ( lambda check : check [ ' name ' ] == self . name , records ) )
2015-12-13 09:34:12 +00:00
if check :
r = check [ 0 ]
if r [ ' type ' ] in ( ' A ' , ' AAAA ' , ' CNAME ' ) :
2016-07-01 19:41:41 +00:00
return { ' status ' : ' error ' , ' msg ' : ' Record already exists with type " A " , " AAAA " or " CNAME " ' }
2015-12-13 09:34:12 +00:00
# continue if the record is ready to be added
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
2016-06-07 10:05:41 +00:00
if NEW_SCHEMA :
data = { " rrsets " : [
{
2016-11-17 14:04:07 +00:00
" name " : self . name . rstrip ( ' . ' ) + ' . ' ,
2016-06-07 10:05:41 +00:00
" type " : self . type ,
" changetype " : " REPLACE " ,
" ttl " : self . ttl ,
" records " : [
{
" content " : self . data ,
" disabled " : self . status ,
}
]
}
]
}
else :
data = { " rrsets " : [
{
" name " : self . name ,
" type " : self . type ,
" changetype " : " REPLACE " ,
" records " : [
{
" content " : self . data ,
" disabled " : self . status ,
" name " : self . name ,
" ttl " : self . ttl ,
" type " : self . type
}
]
}
]
}
2015-12-13 09:34:12 +00:00
try :
2018-04-01 00:57:41 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} ' . format ( domain ) ) , headers = headers , method = ' PATCH ' , data = data )
2015-12-13 09:34:12 +00:00
logging . debug ( jdata )
return { ' status ' : ' ok ' , ' msg ' : ' Record was added successfully ' }
2018-03-30 06:49:35 +00:00
except Exception as e :
2018-04-01 00:57:41 +00:00
logging . error ( " Cannot add record {0} / {1} / {2} to domain {3} . DETAIL: {4} " . format ( self . name , self . type , self . data , domain , e ) )
2015-12-13 09:34:12 +00:00
return { ' status ' : ' error ' , ' msg ' : ' There was something wrong, please contact administrator ' }
def compare ( self , domain_name , new_records ) :
"""
Compare new records with current powerdns record data
Input is a list of hashes ( records )
"""
# get list of current records we have in powerdns
current_records = self . get_record_data ( domain_name ) [ ' records ' ]
2016-08-23 02:52:35 +00:00
2015-12-13 09:34:12 +00:00
# convert them to list of list (just has [name, type]) instead of list of hash
# to compare easier
list_current_records = [ [ x [ ' name ' ] , x [ ' type ' ] ] for x in current_records ]
list_new_records = [ [ x [ ' name ' ] , x [ ' type ' ] ] for x in new_records ]
# get list of deleted records
# they are the records which exist in list_current_records but not in list_new_records
list_deleted_records = [ x for x in list_current_records if x not in list_new_records ]
# convert back to list of hash
2018-01-23 09:08:50 +00:00
deleted_records = [ x for x in current_records if [ x [ ' name ' ] , x [ ' type ' ] ] in list_deleted_records and ( x [ ' type ' ] in app . config [ ' RECORDS_ALLOW_EDIT ' ] and x [ ' type ' ] != ' SOA ' ) ]
2015-12-13 09:34:12 +00:00
# return a tuple
return deleted_records , new_records
def apply ( self , domain , post_records ) :
"""
Apply record changes to domain
"""
2016-08-19 23:04:20 +00:00
records = [ ]
for r in post_records :
r_name = domain if r [ ' record_name ' ] in [ ' @ ' , ' ' ] else r [ ' record_name ' ] + ' . ' + domain
r_type = r [ ' record_type ' ]
if PRETTY_IPV6_PTR : # only if activated
if NEW_SCHEMA : # only if new schema
if r_type == ' PTR ' : # only ptr
if ' : ' in r [ ' record_name ' ] : # dirty ipv6 check
r_name = r [ ' record_name ' ]
2018-03-01 07:26:29 +00:00
2016-08-19 23:04:20 +00:00
record = {
" name " : r_name ,
" type " : r_type ,
" content " : r [ ' record_data ' ] ,
" disabled " : True if r [ ' record_status ' ] == ' Disabled ' else False ,
" ttl " : int ( r [ ' record_ttl ' ] ) if r [ ' record_ttl ' ] else 3600 ,
}
records . append ( record )
2018-03-01 07:26:29 +00:00
2016-08-19 23:04:20 +00:00
deleted_records , new_records = self . compare ( domain , records )
2015-12-13 09:34:12 +00:00
records = [ ]
for r in deleted_records :
2016-11-17 14:04:07 +00:00
r_name = r [ ' name ' ] . rstrip ( ' . ' ) + ' . ' if NEW_SCHEMA else r [ ' name ' ]
2016-08-19 23:04:20 +00:00
r_type = r [ ' type ' ]
if PRETTY_IPV6_PTR : # only if activated
if NEW_SCHEMA : # only if new schema
if r_type == ' PTR ' : # only ptr
if ' : ' in r [ ' name ' ] : # dirty ipv6 check
r_name = dns . reversename . from_address ( r [ ' name ' ] ) . to_text ( )
2018-03-01 07:26:29 +00:00
2015-12-13 09:34:12 +00:00
record = {
2016-08-19 23:04:20 +00:00
" name " : r_name ,
" type " : r_type ,
2015-12-13 09:34:12 +00:00
" changetype " : " DELETE " ,
" records " : [
]
}
records . append ( record )
2016-06-28 17:22:11 +00:00
2015-12-13 09:34:12 +00:00
postdata_for_delete = { " rrsets " : records }
records = [ ]
for r in new_records :
2016-06-07 10:05:41 +00:00
if NEW_SCHEMA :
2016-11-17 14:04:07 +00:00
r_name = r [ ' name ' ] . rstrip ( ' . ' ) + ' . '
2016-08-19 23:04:20 +00:00
r_type = r [ ' type ' ]
if PRETTY_IPV6_PTR : # only if activated
if r_type == ' PTR ' : # only ptr
if ' : ' in r [ ' name ' ] : # dirty ipv6 check
r_name = r [ ' name ' ]
2016-06-07 10:05:41 +00:00
record = {
2016-08-19 23:04:20 +00:00
" name " : r_name ,
" type " : r_type ,
2016-06-07 10:05:41 +00:00
" changetype " : " REPLACE " ,
2016-07-27 15:01:23 +00:00
" ttl " : r [ ' ttl ' ] ,
2016-06-07 10:05:41 +00:00
" records " : [
{
" content " : r [ ' content ' ] ,
" disabled " : r [ ' disabled ' ] ,
}
]
}
else :
record = {
" name " : r [ ' name ' ] ,
" type " : r [ ' type ' ] ,
" changetype " : " REPLACE " ,
" records " : [
{
" content " : r [ ' content ' ] ,
" disabled " : r [ ' disabled ' ] ,
" name " : r [ ' name ' ] ,
" ttl " : r [ ' ttl ' ] ,
" type " : r [ ' type ' ] ,
" priority " : 10 , # priority field for pdns 3.4.1. https://doc.powerdns.com/md/authoritative/upgrading/
}
]
}
2015-12-13 09:34:12 +00:00
records . append ( record )
2016-02-11 09:54:15 +00:00
# Adjustment to add multiple records which described in https://github.com/ngoduykhanh/PowerDNS-Admin/issues/5#issuecomment-181637576
final_records = [ ]
2016-07-27 15:01:23 +00:00
records = sorted ( records , key = lambda item : ( item [ " name " ] , item [ " type " ] , item [ " changetype " ] ) )
for key , group in itertools . groupby ( records , lambda item : ( item [ " name " ] , item [ " type " ] , item [ " changetype " ] ) ) :
2016-07-26 18:34:56 +00:00
if NEW_SCHEMA :
2016-08-19 23:04:20 +00:00
r_name = key [ 0 ]
r_type = key [ 1 ]
r_changetype = key [ 2 ]
2018-03-01 07:26:29 +00:00
2016-08-19 23:04:20 +00:00
if PRETTY_IPV6_PTR : # only if activated
if r_type == ' PTR ' : # only ptr
if ' : ' in r_name : # dirty ipv6 check
r_name = dns . reversename . from_address ( r_name ) . to_text ( )
2018-03-01 07:26:29 +00:00
2016-06-08 04:00:55 +00:00
new_record = {
2016-08-19 23:04:20 +00:00
" name " : r_name ,
" type " : r_type ,
" changetype " : r_changetype ,
2016-07-27 15:01:23 +00:00
" ttl " : None ,
2016-06-08 04:00:55 +00:00
" records " : [ ]
}
for item in group :
temp_content = item [ ' records ' ] [ 0 ] [ ' content ' ]
temp_disabled = item [ ' records ' ] [ 0 ] [ ' disabled ' ]
if key [ 1 ] in [ ' MX ' , ' CNAME ' , ' SRV ' , ' NS ' ] :
if temp_content . strip ( ) [ - 1 : ] != ' . ' :
temp_content + = ' . '
2016-07-27 15:01:23 +00:00
if new_record [ ' ttl ' ] is None :
new_record [ ' ttl ' ] = item [ ' ttl ' ]
2016-06-08 04:00:55 +00:00
new_record [ ' records ' ] . append ( {
" content " : temp_content ,
" disabled " : temp_disabled
} )
final_records . append ( new_record )
2016-08-23 02:52:35 +00:00
2016-07-26 18:34:56 +00:00
else :
2016-08-23 02:52:35 +00:00
2016-06-07 10:05:41 +00:00
final_records . append ( {
" name " : key [ 0 ] ,
" type " : key [ 1 ] ,
2016-07-26 18:34:56 +00:00
" changetype " : key [ 2 ] ,
2016-06-07 10:05:41 +00:00
" records " : [
{
" content " : item [ ' records ' ] [ 0 ] [ ' content ' ] ,
" disabled " : item [ ' records ' ] [ 0 ] [ ' disabled ' ] ,
" name " : key [ 0 ] ,
" ttl " : item [ ' records ' ] [ 0 ] [ ' ttl ' ] ,
" type " : key [ 1 ] ,
" priority " : 10 ,
} for item in group
]
} )
2016-02-11 09:54:15 +00:00
postdata_for_new = { " rrsets " : final_records }
2018-01-22 15:22:19 +00:00
logging . info ( postdata_for_new )
logging . info ( postdata_for_delete )
2018-03-31 01:21:02 +00:00
logging . info ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} ' . format ( domain ) ) )
2015-12-13 09:34:12 +00:00
try :
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
2018-03-31 01:21:02 +00:00
jdata1 = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} ' . format ( domain ) ) , headers = headers , method = ' PATCH ' , data = postdata_for_delete )
jdata2 = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} ' . format ( domain ) ) , headers = headers , method = ' PATCH ' , data = postdata_for_new )
2015-12-13 09:34:12 +00:00
if ' error ' in jdata2 . keys ( ) :
logging . error ( ' Cannot apply record changes. ' )
logging . debug ( jdata2 [ ' error ' ] )
return { ' status ' : ' error ' , ' msg ' : jdata2 [ ' error ' ] }
else :
2016-11-17 10:35:09 +00:00
self . auto_ptr ( domain , new_records , deleted_records )
2018-04-12 04:18:44 +00:00
self . update_db_serial ( domain )
2015-12-13 09:34:12 +00:00
logging . info ( ' Record was applied successfully. ' )
return { ' status ' : ' ok ' , ' msg ' : ' Record was applied successfully ' }
2018-03-30 06:49:35 +00:00
except Exception as e :
2018-03-31 01:21:02 +00:00
logging . error ( " Cannot apply record changes to domain {0} . DETAIL: {1} " . format ( e , domain ) )
2015-12-13 09:34:12 +00:00
return { ' status ' : ' error ' , ' msg ' : ' There was something wrong, please contact administrator ' }
2016-11-17 10:35:09 +00:00
def auto_ptr ( self , domain , new_records , deleted_records ) :
2016-11-21 12:46:54 +00:00
"""
Add auto - ptr records
"""
domain_obj = Domain . query . filter ( Domain . name == domain ) . first ( )
domain_auto_ptr = DomainSetting . query . filter ( DomainSetting . domain == domain_obj ) . filter ( DomainSetting . setting == ' auto_ptr ' ) . first ( )
domain_auto_ptr = strtobool ( domain_auto_ptr . value ) if domain_auto_ptr else False
2016-11-21 15:52:54 +00:00
2016-11-21 12:46:54 +00:00
system_auto_ptr = Setting . query . filter ( Setting . name == ' auto_ptr ' ) . first ( )
system_auto_ptr = strtobool ( system_auto_ptr . value )
2016-11-21 18:44:10 +00:00
2016-11-21 12:46:54 +00:00
if system_auto_ptr or domain_auto_ptr :
2016-11-17 10:35:09 +00:00
try :
2016-11-16 14:15:35 +00:00
d = Domain ( )
2016-11-16 13:12:40 +00:00
for r in new_records :
if r [ ' type ' ] in [ ' A ' , ' AAAA ' ] :
2016-11-16 14:15:35 +00:00
r_name = r [ ' name ' ] + ' . '
2016-11-16 13:12:40 +00:00
r_content = r [ ' content ' ]
2016-11-21 18:44:10 +00:00
reverse_host_address = dns . reversename . from_address ( r_content ) . to_text ( )
domain_reverse_name = d . get_reverse_domain_name ( reverse_host_address )
2016-11-16 13:12:40 +00:00
d . create_reverse_domain ( domain , domain_reverse_name )
2016-11-17 14:04:07 +00:00
self . name = dns . reversename . from_address ( r_content ) . to_text ( ) . rstrip ( ' . ' )
2016-11-16 14:15:35 +00:00
self . type = ' PTR '
self . status = r [ ' disabled ' ]
self . ttl = r [ ' ttl ' ]
self . data = r_name
self . add ( domain_reverse_name )
2016-11-17 10:37:09 +00:00
for r in deleted_records :
if r [ ' type ' ] in [ ' A ' , ' AAAA ' ] :
r_name = r [ ' name ' ] + ' . '
r_content = r [ ' content ' ]
2016-11-21 18:44:10 +00:00
reverse_host_address = dns . reversename . from_address ( r_content ) . to_text ( )
domain_reverse_name = d . get_reverse_domain_name ( reverse_host_address )
self . name = reverse_host_address
2016-11-17 10:37:09 +00:00
self . type = ' PTR '
self . data = r_content
self . delete ( domain_reverse_name )
return { ' status ' : ' ok ' , ' msg ' : ' Auto-PTR record was updated successfully ' }
except Exception as e :
2018-04-01 00:57:41 +00:00
logging . error ( " Cannot update auto-ptr record changes to domain {0} . DETAIL: {1} " . format ( domain , e ) )
2016-11-17 10:37:09 +00:00
return { ' status ' : ' error ' , ' msg ' : ' Auto-PTR creation failed. There was something wrong, please contact administrator. ' }
2015-12-13 09:34:12 +00:00
def delete ( self , domain ) :
"""
Delete a record from domain
"""
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
data = { " rrsets " : [
{
2016-11-17 14:04:07 +00:00
" name " : self . name . rstrip ( ' . ' ) + ' . ' ,
2015-12-13 09:34:12 +00:00
" type " : self . type ,
" changetype " : " DELETE " ,
2016-08-23 02:52:35 +00:00
" records " : [
2015-12-13 09:34:12 +00:00
]
}
]
}
try :
2018-04-01 00:57:41 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} ' . format ( domain ) ) , headers = headers , method = ' PATCH ' , data = data )
2015-12-13 09:34:12 +00:00
logging . debug ( jdata )
return { ' status ' : ' ok ' , ' msg ' : ' Record was removed successfully ' }
except :
2018-04-01 00:57:41 +00:00
logging . error ( " Cannot remove record {0} / {1} / {2} from domain {3} " . format ( self . name , self . type , self . data , domain ) )
2015-12-13 09:34:12 +00:00
return { ' status ' : ' error ' , ' msg ' : ' There was something wrong, please contact administrator ' }
2018-01-23 09:08:50 +00:00
def is_allowed_edit ( self ) :
2015-12-13 09:34:12 +00:00
"""
2018-01-23 09:08:50 +00:00
Check if record is allowed to edit
2015-12-13 09:34:12 +00:00
"""
return self . type in app . config [ ' RECORDS_ALLOW_EDIT ' ]
2018-01-23 09:08:50 +00:00
def is_allowed_delete ( self ) :
"""
Check if record is allowed to removed
"""
return ( self . type in app . config [ ' RECORDS_ALLOW_EDIT ' ] and self . type != ' SOA ' )
2016-06-20 09:32:14 +00:00
def exists ( self , domain ) :
"""
Check if record is present within domain records , and if it ' s present set self to found record
"""
jdata = self . get_record_data ( domain )
jrecords = jdata [ ' records ' ]
for jr in jrecords :
if jr [ ' name ' ] == self . name :
self . name = jr [ ' name ' ]
self . type = jr [ ' type ' ]
self . status = jr [ ' disabled ' ]
self . ttl = jr [ ' ttl ' ]
self . data = jr [ ' content ' ]
self . priority = 10
return True
return False
def update ( self , domain , content ) :
"""
Update single record
"""
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
if NEW_SCHEMA :
data = { " rrsets " : [
{
" name " : self . name + ' . ' ,
" type " : self . type ,
" ttl " : self . ttl ,
" changetype " : " REPLACE " ,
" records " : [
{
" content " : content ,
" disabled " : self . status ,
}
]
}
]
}
else :
data = { " rrsets " : [
{
" name " : self . name ,
" type " : self . type ,
" changetype " : " REPLACE " ,
" records " : [
{
" content " : content ,
" disabled " : self . status ,
" name " : self . name ,
" ttl " : self . ttl ,
" type " : self . type ,
" priority " : 10
}
]
}
]
}
try :
2018-04-01 00:57:41 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/localhost/zones/ {0} ' . format ( domain ) ) , headers = headers , method = ' PATCH ' , data = data )
logging . debug ( " dyndns data: {0} " . format ( data ) )
2016-06-20 09:32:14 +00:00
return { ' status ' : ' ok ' , ' msg ' : ' Record was updated successfully ' }
2018-03-30 06:49:35 +00:00
except Exception as e :
2018-04-01 00:57:41 +00:00
logging . error ( " Cannot add record {0} / {1} / {2} to domain {3} . DETAIL: {4} " . format ( self . name , self . type , self . data , domain , e ) )
2016-06-20 09:32:14 +00:00
return { ' status ' : ' error ' , ' msg ' : ' There was something wrong, please contact administrator ' }
2018-04-12 04:18:44 +00:00
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 ) }
2015-12-13 09:34:12 +00:00
class Server ( object ) :
"""
This is not a model , it ' s just an object
which be assigned data from PowerDNS API
"""
def __init__ ( self , server_id = None , server_config = None ) :
self . server_id = server_id
self . server_config = server_config
def get_config ( self ) :
"""
Get server config
"""
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
2016-08-23 02:52:35 +00:00
2015-12-13 09:34:12 +00:00
try :
2018-04-01 00:57:41 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/ {0} /config ' . format ( self . server_id ) ) , headers = headers , method = ' GET ' )
2015-12-13 09:34:12 +00:00
return jdata
except :
logging . error ( " Can not get server configuration. " )
logging . debug ( traceback . format_exc ( ) )
return [ ]
def get_statistic ( self ) :
"""
Get server statistics
"""
headers = { }
headers [ ' X-API-Key ' ] = PDNS_API_KEY
2016-06-07 06:50:31 +00:00
2015-12-13 09:34:12 +00:00
try :
2018-04-01 00:57:41 +00:00
jdata = utils . fetch_json ( urljoin ( PDNS_STATS_URL , API_EXTENDED_URL + ' /servers/ {0} /statistics ' . format ( self . server_id ) ) , headers = headers , method = ' GET ' )
2015-12-13 09:34:12 +00:00
return jdata
except :
logging . error ( " Can not get server statistics. " )
logging . debug ( traceback . format_exc ( ) )
return [ ]
class History ( db . Model ) :
id = db . Column ( db . Integer , primary_key = True )
msg = db . Column ( db . String ( 256 ) )
2016-09-13 07:07:48 +00:00
detail = db . Column ( db . Text ( ) . with_variant ( db . Text ( length = 2 * * 24 - 2 ) , ' mysql ' ) )
2015-12-13 09:34:12 +00:00
created_by = db . Column ( db . String ( 128 ) )
created_on = db . Column ( db . DateTime , default = datetime . utcnow )
def __init__ ( self , id = None , msg = None , detail = None , created_by = None ) :
self . id = id
self . msg = msg
self . detail = detail
self . created_by = created_by
def __repr__ ( self ) :
2018-04-01 00:57:41 +00:00
return ' <History {0} > ' . format ( self . msg )
2015-12-13 09:34:12 +00:00
def add ( self ) :
"""
Add an event to history table
"""
h = History ( )
h . msg = self . msg
h . detail = self . detail
h . created_by = self . created_by
db . session . add ( h )
db . session . commit ( )
def remove_all ( self ) :
"""
Remove all history from DB
"""
try :
num_rows_deleted = db . session . query ( History ) . delete ( )
db . session . commit ( )
logging . info ( " Removed all history " )
return True
except :
db . session . rollback ( )
logging . error ( " Cannot remove history " )
logging . debug ( traceback . format_exc ( ) )
return False
class Setting ( db . Model ) :
id = db . Column ( db . Integer , primary_key = True )
name = db . Column ( db . String ( 64 ) )
value = db . Column ( db . String ( 256 ) )
def __init__ ( self , id = None , name = None , value = None ) :
self . id = id
self . name = name
self . value = value
2016-08-23 02:52:35 +00:00
2016-04-11 09:40:44 +00:00
# allow database autoincrement to do its own ID assignments
def __init__ ( self , name = None , value = None ) :
self . id = None
self . name = name
2016-08-23 02:52:35 +00:00
self . value = value
2015-12-13 09:34:12 +00:00
def set_mainteance ( self , mode ) :
"""
mode = True / False
"""
mode = str ( mode )
maintenance = Setting . query . filter ( Setting . name == ' maintenance ' ) . first ( )
try :
if maintenance :
if maintenance . value != mode :
maintenance . value = mode
db . session . commit ( )
return True
else :
s = Setting ( name = ' maintenance ' , value = mode )
db . session . add ( s )
db . session . commit ( )
return True
except :
2018-04-01 00:57:41 +00:00
logging . error ( ' Cannot set maintenance to {0} ' . format ( mode ) )
2015-12-13 09:34:12 +00:00
logging . debug ( traceback . format_exc ( ) )
db . session . rollback ( )
return False
2016-04-29 21:36:37 +00:00
def toggle ( self , setting ) :
setting = str ( setting )
current_setting = Setting . query . filter ( Setting . name == setting ) . first ( )
try :
if current_setting :
if current_setting . value == " True " :
current_setting . value = " False "
else :
current_setting . value = " True "
db . session . commit ( )
return True
else :
2018-04-01 00:57:41 +00:00
logging . error ( ' Setting {0} does not exist ' . format ( setting ) )
2016-04-29 21:36:37 +00:00
return False
except :
2018-04-01 00:57:41 +00:00
logging . error ( ' Cannot toggle setting {0} ' . format ( setting ) )
2016-04-29 21:36:37 +00:00
logging . debug ( traceback . format_exec ( ) )
db . session . rollback ( )
2016-06-09 01:23:08 +00:00
return False
2016-08-23 02:52:35 +00:00
2016-06-09 01:23:08 +00:00
def set ( self , setting , value ) :
setting = str ( setting )
new_value = str ( value )
current_setting = Setting . query . filter ( Setting . name == setting ) . first ( )
try :
if current_setting :
current_setting . value = new_value
db . session . commit ( )
return True
else :
2018-04-01 00:57:41 +00:00
logging . error ( ' Setting {0} does not exist ' . format ( setting ) )
2016-06-09 01:23:08 +00:00
return False
except :
2018-04-01 00:57:41 +00:00
logging . error ( ' Cannot edit setting {0} ' . format ( setting ) )
2016-06-09 01:23:08 +00:00
logging . debug ( traceback . format_exec ( ) )
db . session . rollback ( )
2016-07-01 19:41:41 +00:00
return False
2018-01-22 15:22:19 +00:00
class DomainTemplate ( db . Model ) :
__tablename__ = " domain_template "
id = db . Column ( db . Integer , primary_key = True )
name = db . Column ( db . String ( 255 ) , index = True , unique = True )
description = db . Column ( db . String ( 255 ) )
records = db . relationship ( ' DomainTemplateRecord ' , back_populates = ' template ' , cascade = " all, delete-orphan " )
def __repr__ ( self ) :
2018-04-01 00:57:41 +00:00
return ' <DomainTemplate {0} > ' . format ( self . name )
2018-01-22 15:22:19 +00:00
def __init__ ( self , name = None , description = None ) :
self . id = None
self . name = name
self . description = description
def replace_records ( self , records ) :
try :
self . records = [ ]
for record in records :
self . records . append ( record )
db . session . commit ( )
return { ' status ' : ' ok ' , ' msg ' : ' Template records have been modified ' }
2018-03-31 01:21:02 +00:00
except Exception as e :
logging . error ( ' Cannot create template records Error: {0} ' . format ( e ) )
2018-01-22 15:22:19 +00:00
db . session . rollback ( )
return { ' status ' : ' error ' , ' msg ' : ' Can not create template records ' }
def create ( self ) :
try :
db . session . add ( self )
db . session . commit ( )
return { ' status ' : ' ok ' , ' msg ' : ' Template has been created ' }
2018-03-31 01:21:02 +00:00
except Exception as e :
logging . error ( ' Can not update domain template table. Error: {0} ' . format ( e ) )
2018-01-22 15:22:19 +00:00
db . session . rollback ( )
return { ' status ' : ' error ' , ' msg ' : ' Can not update domain template table ' }
def delete_template ( self ) :
try :
self . records = [ ]
db . session . delete ( self )
db . session . commit ( )
return { ' status ' : ' ok ' , ' msg ' : ' Template has been deleted ' }
2018-03-31 01:21:02 +00:00
except Exception as e :
logging . error ( ' Can not delete domain template. Error: {0} ' . format ( e ) )
2018-01-22 15:22:19 +00:00
db . session . rollback ( )
return { ' status ' : ' error ' , ' msg ' : ' Can not delete domain template ' }
class DomainTemplateRecord ( db . Model ) :
__tablename__ = " domain_template_record "
id = db . Column ( db . Integer , primary_key = True )
name = db . Column ( db . String ( 255 ) )
type = db . Column ( db . String ( 64 ) )
ttl = db . Column ( db . Integer )
2018-04-12 04:44:56 +00:00
data = db . Column ( db . Text )
2018-01-22 15:22:19 +00:00
status = db . Column ( db . Boolean )
template_id = db . Column ( db . Integer , db . ForeignKey ( ' domain_template.id ' ) )
template = db . relationship ( ' DomainTemplate ' , back_populates = ' records ' )
def __repr__ ( self ) :
2018-04-01 00:57:41 +00:00
return ' <DomainTemplateRecord {0} > ' . format ( self . id )
2018-01-22 15:22:19 +00:00
def __init__ ( self , id = None , name = None , type = None , ttl = None , data = None , status = None ) :
self . id = id
self . name = name
self . type = type
self . ttl = ttl
self . data = data
self . status = status
def apply ( self ) :
try :
db . session . commit ( )
2018-03-31 01:21:02 +00:00
except Exception as e :
logging . error ( ' Can not update domain template table. Error: {0} ' . format ( e ) )
2018-01-22 15:22:19 +00:00
db . session . rollback ( )
return { ' status ' : ' error ' , ' msg ' : ' Can not update domain template table ' }