Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Admin user table, admin logging, multi user admin #207

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion dsiprouter.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export IPV6_ENABLED=0
export DSIP_SYSTEM_CONFIG_DIR="/etc/dsiprouter"
export DSIP_KAMAILIO_CONFIG_DIR="${DSIP_PROJECT_DIR}/kamailio"
export DSIP_KAMAILIO_CONFIG_FILE="${DSIP_SYSTEM_CONFIG_DIR}/kamailio51_dsiprouter.cfg"
export DSIP_ADMIN_DIR="${DSIP_KAMAILIO_CONFIG_DIR}/admin"
export DSIP_DEFAULTS_DIR="${DSIP_KAMAILIO_CONFIG_DIR}/defaults"
export DSIP_CONFIG_FILE="${DSIP_PROJECT_DIR}/gui/settings.py"
export DSIP_RUN_DIR="/var/run/dsiprouter"
Expand Down Expand Up @@ -209,7 +210,7 @@ ZXIKClRoYW5rcyB0byBvdXIgc3BvbnNvcjogU2t5ZXRlbCAoc2t5ZXRlbC5jb20pCg==" \

# Cleanup exported variables on exit
function cleanupAndExit {
unset DSIP_PROJECT_DIR DSIP_INSTALL_DIR DSIP_KAMAILIO_CONFIG_DIR DSIP_KAMAILIO_CONFIG DSIP_DEFAULTS_DIR SYSTEM_KAMAILIO_CONFIG_DIR DSIP_CONFIG_FILE
unset DSIP_PROJECT_DIR DSIP_INSTALL_DIR DSIP_KAMAILIO_CONFIG_DIR DSIP_KAMAILIO_CONFIG DSIP_ADMIN_DIR DSIP_DEFAULTS_DIR SYSTEM_KAMAILIO_CONFIG_DIR DSIP_CONFIG_FILE
unset REQ_PYTHON_MAJOR_VER DISTRO DISTRO_VER PYTHON_CMD AWS_ENABLED PATH_UPDATE_FILE SYSTEM_RTPENGINE_CONFIG_DIR SYSTEM_RTPENGINE_CONFIG_FILE SERVERNAT
unset RTPENGINE_VER SRC_DIR DSIP_SYSTEM_CONFIG_DIR BACKUPS_DIR DSIP_RUN_DIR KAM_VERSION CLOUD_INSTALL_LOG DEBUG IPV6_ENABLED
unset MYSQL_ROOT_PASSWORD MYSQL_ROOT_USERNAME MYSQL_ROOT_DATABASE MYSQL_KAM_PASSWORD MYSQL_KAM_USERNAME MYSQL_KAM_DATABASE
Expand Down Expand Up @@ -521,6 +522,9 @@ function configureKamailio {
mysql --user="$MYSQL_KAM_USERNAME" --password="$MYSQL_KAM_PASSWORD" $MYSQL_KAM_DATABASE < $sqlscript
fi

# Install schema for admin users table
mysql -s -N --user="$MYSQL_ROOT_USERNAME" --password="$MYSQL_ROOT_PASSWORD" $MYSQL_KAM_DATABASE < ${DSIP_ADMIN_DIR}/dsip_admin.sql

# Install schema for custom LCR logic
mysql -s -N --user="$MYSQL_ROOT_USERNAME" --password="$MYSQL_ROOT_PASSWORD" $MYSQL_KAM_DATABASE < ${DSIP_DEFAULTS_DIR}/lcr.sql

Expand Down
15 changes: 15 additions & 0 deletions gui/database/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@ def __init__(self, groupid, prefix, gwlist, notes=''):
pass


class AdminUsers(object):
"""
Schema for admin_users\n
"""

def __init__(self, admin_id, admin_username, admin_hash):
self.admin_id = admin_id
self.admin_username = admin_username
self.admin_hash = admin_hash

pass


class OutboundRoutes(object):
"""
Schema for dr_rules table\n
Expand Down Expand Up @@ -404,6 +417,7 @@ def loadSession():

dr_gateways = Table('dr_gateways', metadata, autoload=True)
address = Table('address', metadata, autoload=True)
admin_users = Table('admin_users', metadata, autoload=True)
outboundroutes = Table('dr_rules', metadata, autoload=True)
inboundmapping = Table('dr_rules', metadata, autoload=True)
subscriber = Table('subscriber', metadata, autoload=True)
Expand Down Expand Up @@ -431,6 +445,7 @@ def loadSession():

mapper(Gateways, dr_gateways)
mapper(Address, address)
mapper(AdminUsers, admin_users)
mapper(InboundMapping, inboundmapping)
mapper(OutboundRoutes, outboundroutes)
mapper(dSIPDomainMapping, dsip_domain_mapping)
Expand Down
76 changes: 62 additions & 14 deletions gui/dsiprouter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python3

import os, re, json, subprocess, urllib.parse, glob,datetime, csv, logging, signal
import os, re, json, subprocess, urllib.parse, glob,datetime, csv, logging, signal, hashlib
import logging.handlers
from copy import copy
from flask import Flask, render_template, request, redirect, abort, flash, session, url_for, send_from_directory, g
from flask_script import Manager, Server
Expand All @@ -13,7 +14,7 @@
from shared import getInternalIP, getExternalIP, updateConfig, getCustomRoutes, debugException, debugEndpoint, \
stripDictVals, strFieldsToDict, dictToStrFields, allowed_file, showError, hostToIP, IO, status
from database import loadSession, Gateways, Address, InboundMapping, OutboundRoutes, Subscribers, dSIPLCR, \
UAC, GatewayGroups, Domain, DomainAttrs, dSIPDomainMapping, dSIPMultiDomainMapping, Dispatcher, dSIPMaintModes
UAC, GatewayGroups, AdminUsers, Domain, DomainAttrs, dSIPDomainMapping, dSIPMultiDomainMapping, Dispatcher, dSIPMaintModes
from modules import flowroute
from modules.domain.domain_routes import domains
import globals
Expand All @@ -27,7 +28,6 @@
db = loadSession()
numbers_api = flowroute.Numbers()


# TODO: unit testing per component

@app.before_first_request
Expand Down Expand Up @@ -74,27 +74,38 @@ def index():
error = "server"
return showError(type=error)


@app.route('/favicon.ico')
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'),
'favicon.ico', mimetype='image/vnd.microsoft.icon')



@app.route('/login', methods=['POST'])
def login():
try:
if (settings.DEBUG):
debugEndpoint()

form = stripDictVals(request.form.to_dict())

txtUser = form['username']
txtPassword = form['password']
txtPasswordHash = hashlib.md5(txtPassword.encode('utf-8')).hexdigest()

if form['password'] == settings.PASSWORD and form['username'] == settings.USERNAME:
validAdmin = db.query(AdminUsers).filter(
AdminUsers.admin_username == txtUser,
AdminUsers.admin_hash == txtPasswordHash
).count()

logAdmin = initiateLogger()
if validAdmin == 1:
session['logged_in'] = True
session['username'] = form['username']
session['username'] = txtUser
logAdmin.debug(settings.LOG_TAG + session['username'] + ' - Login successful')
else:
flash('wrong username or password!')
flash('There seems to be a problem. Contact technical support.')
logAdmin.debug(settings.LOG_TAG + txtUser + ' - Login failure')
return render_template('index.html', version=settings.VERSION), 403

return redirect(url_for('index'))


Expand All @@ -113,6 +124,9 @@ def logout():
try:
if (settings.DEBUG):
debugEndpoint()

logAdmin = initiateLogger()
logAdmin.debug(settings.LOG_TAG + ' logout ' + session['username'])

session.pop('logged_in', None)
session.pop('username', None)
Expand Down Expand Up @@ -156,6 +170,9 @@ def displayCarrierGroups(gwgroup=None):
GatewayGroups.id, GatewayGroups.gwlist, GatewayGroups.description,
UAC.auth_username, UAC.auth_password, UAC.realm).all()
db.close()
logAdmin = initiateLogger()
logAdmin.debug(settings.LOG_TAG + session['username'] + ' - Display Carriers')

return render_template('carriergroups.html', rows=res, API_URL=request.url_root)

except sql_exceptions.SQLAlchemyError as ex:
Expand Down Expand Up @@ -221,7 +238,7 @@ def addUpdateCarrierGroups():
Uacreg = UAC(gwgroup, auth_username, auth_password, auth_domain, auth_proxy,
settings.EXTERNAL_IP_ADDR, auth_domain)
#Add auth_domain(aka registration server) to the gateway list
Addr = Address(name + "-uac", hostToIP(auth_domain), 32, str(settings.FLT_CARRIER), gwgroup=str(gwgroup))
Addr = Address(name + "-uac", hostToIP(auth_domain), 32, settings.FLT_CARRIER, gwgroup=gwgroup)
db.add(Uacreg)
db.add(Addr)

Expand Down Expand Up @@ -1025,6 +1042,8 @@ def displayInboundMapping():
debugException(ex, log_ex=False, print_ex=True, showstack=False)
return showError(type="http", code=ex.status_code, msg="Flowroute Credentials Not Valid")

logAdmin = initiateLogger()
logAdmin.debug(settings.LOG_TAG + session['username'] + ' - Display Inbound Mappings')
return render_template('inboundmapping.html', rows=res, gwlist=gateways, imported_dids=dids)

except sql_exceptions.SQLAlchemyError as ex:
Expand Down Expand Up @@ -1089,21 +1108,24 @@ def addUpdateInboundMapping():
gateways.append(gw)
gwlist = ','.join(gateways)


logMsg = ''
# Adding
if not ruleid:
# don't allow duplicate entries
if db.query(InboundMapping).filter(InboundMapping.prefix == prefix).filter(InboundMapping.groupid == settings.FLT_INBOUND).scalar():
raise http_exceptions.BadRequest("Duplicate DID's are not allowed")
IMap = InboundMapping(settings.FLT_INBOUND, prefix, gwlist, notes)
db.add(IMap)

logMsg = 'Added inbound rule: ' + str(prefix)
# Updating
else:
db.query(InboundMapping).filter(InboundMapping.ruleid == ruleid).update(
{'prefix': prefix, 'gwlist': gwlist, 'description': notes}, synchronize_session=False)

logMsg = 'Updated inbound rule: ' + str(prefix)
db.commit()
logAdmin = initiateLogger()
logAdmin.debug(settings.LOG_TAG + session['username'] + ' - ' + logMsg)

globals.reload_required = True
return displayInboundMapping()

Expand Down Expand Up @@ -1154,6 +1176,9 @@ def deleteInboundMapping():
d = db.query(InboundMapping).filter(InboundMapping.ruleid == ruleid)
d.delete(synchronize_session=False)
db.commit()

logAdmin = initiateLogger()
logAdmin.debug(settings.LOG_TAG + session['username'] + ' - Deleted rule id: ' + str(ruleid))

globals.reload_required = True
return displayInboundMapping()
Expand Down Expand Up @@ -1197,6 +1222,9 @@ def processInboundMappingImport(filename,groupid,pbxid,notes,db):
IMap = InboundMapping(groupid, row[0], pbxid, notes)
db.add(IMap)

logAdmin = initiateLogger()
logAdmin.debug(settings.LOG_TAG + session['username'] + ' - Imported DID\'s')

db.commit()


Expand Down Expand Up @@ -1409,6 +1437,9 @@ def displayOutboundRoutes():
teleblock["media_ip"] = settings.TELEBLOCK_MEDIA_IP
teleblock["media_port"] = settings.TELEBLOCK_MEDIA_PORT

logAdmin = initiateLogger()
logAdmin.debug(settings.LOG_TAG + session['username'] + ' - Display global outbound routes')

return render_template('outboundroutes.html', rows=rows, teleblock=teleblock, custom_routes='')

except sql_exceptions.SQLAlchemyError as ex:
Expand Down Expand Up @@ -1709,13 +1740,18 @@ def reloadkam():
['kamcmd', 'cfg.seti', 'teleblock', 'media_port', str(settings.TELEBLOCK_MEDIA_PORT)])

session['last_page'] = request.headers['Referer']


status_code = 100
if return_code == 0:
status_code = 1
globals.reload_required = False
else:
status_code = 0
globals.reload_required = prev_reload_val

logAdmin = initiateLogger()
logAdmin.debug(settings.LOG_TAG + session['username'] + ' - Reloaded Kamailio with return status of ' + str(status_code))

return json.dumps({"status": status_code})

except http_exceptions.HTTPException as ex:
Expand Down Expand Up @@ -1884,5 +1920,17 @@ def checkDatabase():
# If not, close DB connection so that the SQL engine can get another one from the pool
db.close()

def initiateLogger():

logger = logging.getLogger('adminLog')
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
if not len(logger.handlers):
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
return logger

if __name__ == "__main__":
initApp(app)
5 changes: 3 additions & 2 deletions gui/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
DSIP_PROTO = 'http'
DSIP_HOST = '0.0.0.0'
DSIP_PORT = 5000
USERNAME = 'admin'
PASSWORD = 'ZmJhM2Y1ODAwNDhl'
DSIP_API_TOKEN = 'neggz1jEn5ei0ZICsNdWSHv9Obdq7VJJukaLp8smlOaZm49NCrHX72MabKCBixLx'
DSIP_API_HOST = ''

Expand Down Expand Up @@ -91,3 +89,6 @@
# AWS = Amazon Web Services, GCP = Google Cloud Platform, AZURE = Microsoft Azure, DO = Digital Ocean
CLOUD_PLATFORM = ''


# Log tagging
LOG_TAG = '[dsip gui]: '
7 changes: 7 additions & 0 deletions kamailio/admin/dsip_admin.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
DROP TABLE IF EXISTS `admin_users`;
CREATE TABLE `admin_users` (
`admin_id` int NOT NULL AUTO_INCREMENT,
`admin_username` varchar(32) NOT NULL,
`admin_hash` varchar(32) NOT NULL,
PRIMARY KEY (`admin_id`)
);