Compare commits
72 Commits
gps
...
pnw_digita
Author | SHA1 | Date | |
---|---|---|---|
|
4685686053 | ||
|
b3f12587c1 | ||
|
1d93ef13ea | ||
|
6d093b4b08 | ||
|
7429db3a03 | ||
|
ea39d5fa6a | ||
|
5f57ceae66 | ||
|
434c9a8e80 | ||
|
38a1ee2c0f | ||
|
6362b8cd5a | ||
|
275af6168d | ||
|
ccc5801b59 | ||
|
d7266ac387 | ||
|
2503e448c6 | ||
|
6258ccc074 | ||
|
3ce2e2c683 | ||
|
017f55eef3 | ||
|
ccaee1015b | ||
|
e71c2046d1 | ||
|
7bea2fbe18 | ||
|
3be127d0f4 | ||
|
9267fef2cb | ||
|
413044aba8 | ||
|
de08cc897e | ||
|
ac7b827c36 | ||
|
3f3b980b98 | ||
|
c16036b58d | ||
|
f94d68ae81 | ||
|
d87c67acb0 | ||
|
06cc28fe82 | ||
|
92e0e2e405 | ||
|
a03a9e1018 | ||
|
c9436e54ec | ||
|
eb0a6b49d4 | ||
|
d8e4351ad3 | ||
|
1b9bb3a83f | ||
|
886a15945d | ||
|
90aa470bb9 | ||
|
37af7aab2a | ||
|
dd0825c005 | ||
|
6ac2ffe091 | ||
|
b6f1a92a60 | ||
|
cb81326da2 | ||
|
d1a7faf29c | ||
|
257232078a | ||
|
09e1fca1cb | ||
|
aa9e68ae5a | ||
|
ed31308b26 | ||
|
6827cda57a | ||
|
fa11f3bdf4 | ||
|
7d890bc504 | ||
|
df1b69612a | ||
|
a60e1b2cac | ||
|
5915463b71 | ||
|
080b88d43f | ||
|
c362e3e675 | ||
|
2af99e7f46 | ||
|
52ac438fcc | ||
|
bda66fbdde | ||
|
16744c61e5 | ||
|
2159d09525 | ||
|
6ddfb2b1c1 | ||
|
8adb80f188 | ||
|
3ed20d48ac | ||
|
bc2b146f79 | ||
|
f1541c7d7d | ||
|
912d875d11 | ||
|
213a5c6d8f | ||
|
a93a61d05b | ||
|
72d0ca87e5 | ||
|
1902234e87 | ||
|
5de722fcf1 |
@ -1,3 +1,5 @@
|
||||
|
||||
### This is a branch of HBLink3 for development of a user authentication system for hotspots for PNW Digital.
|
||||
---
|
||||
### FOR SUPPORT, DISCUSSION, GETTING INVOLVED ###
|
||||
|
||||
|
113
bridge.py
113
bridge.py
@ -42,12 +42,13 @@ from twisted.protocols.basic import NetstringReceiver
|
||||
from twisted.internet import reactor, task
|
||||
|
||||
# Things we import from the main hblink module
|
||||
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, mk_aliases
|
||||
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, mk_aliases, download_burnlist
|
||||
from dmr_utils3.utils import bytes_3, int_id, get_alias
|
||||
from dmr_utils3 import decode, bptc, const
|
||||
import config
|
||||
import log
|
||||
from const import *
|
||||
from hashlib import sha256
|
||||
|
||||
# Stuff for socket reporting
|
||||
import pickle
|
||||
@ -55,8 +56,8 @@ import pickle
|
||||
# The module needs logging, but handlers, etc. are controlled by the parent
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
import os, ast
|
||||
import json, requests
|
||||
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
@ -65,6 +66,86 @@ __license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
##import os, ast
|
||||
def download_config(L_CONFIG_FILE, cli_file):
|
||||
user_man_url = L_CONFIG_FILE['USER_MANAGER']['URL']
|
||||
shared_secret = str(sha256(L_CONFIG_FILE['USER_MANAGER']['SHARED_SECRET'].encode()).hexdigest())
|
||||
config_check = {
|
||||
'get_config':L_CONFIG_FILE['USER_MANAGER']['THIS_SERVER_NAME'],
|
||||
'secret':shared_secret
|
||||
}
|
||||
json_object = json.dumps(config_check, indent = 4)
|
||||
|
||||
try:
|
||||
req = requests.post(user_man_url, data=json_object, headers={'Content-Type': 'application/json'})
|
||||
resp = json.loads(req.text)
|
||||
print(resp)
|
||||
|
||||
## print(type(resp))
|
||||
## conf = config.build_config(resp['config'])
|
||||
## print(conf)
|
||||
## with open('/tmp/conf_telp.cfg', 'w') as f:
|
||||
## f.write(str(resp['config']))
|
||||
## print(resp)
|
||||
iterate_config = resp['peers'].copy()
|
||||
|
||||
corrected_config = resp['config'].copy()
|
||||
corrected_config['SYSTEMS'] = {}
|
||||
corrected_config['LOGGER'] = {}
|
||||
corrected_config['SYSTEMS'].update(iterate_config)
|
||||
corrected_config['LOGGER'].update(L_CONFIG_FILE['LOGGER'])
|
||||
corrected_config['USER_MANAGER'].update(L_CONFIG_FILE['USER_MANAGER'])
|
||||
print(iterate_config)
|
||||
|
||||
## corrected_config = CONFIG_FILE.copy()
|
||||
|
||||
|
||||
## print(corrected_config)
|
||||
## print()
|
||||
## print(iterate_config['config']['SYSTEMS'])
|
||||
## print(resp['config'])
|
||||
## print((iterate_config['test']))
|
||||
## print(corrected_config)
|
||||
|
||||
corrected_config['GLOBAL']['TG1_ACL'] = config.acl_build(corrected_config['GLOBAL']['TG1_ACL'], 16776415)
|
||||
corrected_config['GLOBAL']['TG2_ACL'] = config.acl_build(corrected_config['GLOBAL']['TG2_ACL'], 16776415)
|
||||
corrected_config['GLOBAL']['REG_ACL'] = config.acl_build(corrected_config['GLOBAL']['REG_ACL'], 16776415)
|
||||
corrected_config['GLOBAL']['SUB_ACL'] = config.acl_build(corrected_config['GLOBAL']['SUB_ACL'], 16776415)
|
||||
## corrected_config['SYSTEMS'] = {}
|
||||
for i in iterate_config:
|
||||
print(i)
|
||||
## corrected_config['SYSTEMS'][i] = {}
|
||||
if iterate_config[i]['MODE'] == 'MASTER' or iterate_config[i]['MODE'] == 'PROXY':
|
||||
corrected_config['SYSTEMS'][i]['TG1_ACL'] = config.acl_build(iterate_config[i]['TG1_ACL'], 16776415)
|
||||
corrected_config['SYSTEMS'][i]['TG2_ACL'] = config.acl_build(iterate_config[i]['TG2_ACL'], 16776415)
|
||||
else:
|
||||
corrected_config['SYSTEMS'][i]['RADIO_ID'] = int(iterate_config[i]['RADIO_ID']).to_bytes(4, 'big')
|
||||
corrected_config['SYSTEMS'][i]['TG1_ACL'] = config.acl_build(iterate_config[i]['TG1_ACL'], 16776415)
|
||||
corrected_config['SYSTEMS'][i]['TG2_ACL'] = config.acl_build(iterate_config[i]['TG2_ACL'], 16776415)
|
||||
corrected_config['SYSTEMS'][i]['USE_ACL'] = iterate_config[i]['USE_ACL']
|
||||
corrected_config['SYSTEMS'][i]['SUB_ACL'] = config.acl_build(iterate_config[i]['SUB_ACL'], 16776415)
|
||||
corrected_config['SYSTEMS'][i]['MASTER_SOCKADDR'] = tuple(iterate_config[i]['MASTER_SOCKADDR'])
|
||||
corrected_config['SYSTEMS'][i]['SOCK_ADDR'] = tuple(iterate_config[i]['SOCK_ADDR'])
|
||||
corrected_config['SYSTEMS'][i].update({'STATS':{
|
||||
'CONNECTION': 'NO', # NO, RTPL_SENT, AUTHENTICATED, CONFIG-SENT, YES
|
||||
'CONNECTED': None,
|
||||
'PINGS_SENT': 0,
|
||||
'PINGS_ACKD': 0,
|
||||
'NUM_OUTSTANDING': 0,
|
||||
'PING_OUTSTANDING': False,
|
||||
'LAST_PING_TX_TIME': 0,
|
||||
'LAST_PING_ACK_TIME': 0,
|
||||
}})
|
||||
print(corrected_config)
|
||||
## config.process_acls(corrected_config)
|
||||
## print(corrected_config)
|
||||
print('-------')
|
||||
return corrected_config
|
||||
# For exception, write blank dict
|
||||
except requests.ConnectionError:
|
||||
logger.error('Config server unreachable, defaulting to local config')
|
||||
return config.build_config(cli_file)
|
||||
|
||||
# Module gobal varaibles
|
||||
|
||||
# Dictionary for dynamically mapping unit (subscriber) to a system.
|
||||
@ -1096,8 +1177,28 @@ if __name__ == '__main__':
|
||||
cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/hblink.cfg'
|
||||
|
||||
# Call the external routine to build the configuration dictionary
|
||||
LOCAL_CONFIG = config.build_config(cli_args.CONFIG_FILE)
|
||||
#print(LOCAL_CONFIG)
|
||||
#print(download_config(LOCAL_CONFIG))
|
||||
#if LOCAL_CONFIG['USER_MANAGER']['REMOTE_CONFIG_ENABLED']:
|
||||
#print(download_config(LOCAL_CONFIG)['config'])
|
||||
## CONFIG = config.build_config(download_config(LOCAL_CONFIG))
|
||||
#CONFIG = download_config(LOCAL_CONFIG)
|
||||
#if not LOCAL_CONFIG['USER_MANAGER']['REMOTE_CONFIG_ENABLED']:
|
||||
## print(download_config(LOCAL_CONFIG)['config'])
|
||||
## CONFIG = config.build_config(cli_args.CONFIG_FILE)
|
||||
#print((CONFIG))
|
||||
|
||||
|
||||
## config.process_acls(LOCAL_CONFIG)
|
||||
if LOCAL_CONFIG['USER_MANAGER']['REMOTE_CONFIG_ENABLED']:
|
||||
CONFIG = download_config(LOCAL_CONFIG, cli_args.CONFIG_FILE)
|
||||
else:
|
||||
CONFIG = config.build_config(cli_args.CONFIG_FILE)
|
||||
|
||||
|
||||
## print(CONFIG)
|
||||
|
||||
# Ensure we have a path for the rules file, if one wasn't specified, then use the default (top of file)
|
||||
if not cli_args.RULES_FILE:
|
||||
cli_args.RULES_FILE = os.path.dirname(os.path.abspath(__file__))+'/rules.py'
|
||||
@ -1170,4 +1271,10 @@ if __name__ == '__main__':
|
||||
stream_trimmer = stream_trimmer_task.start(5)
|
||||
stream_trimmer.addErrback(loopingErrHandle)
|
||||
|
||||
|
||||
# Download burn list
|
||||
with open(CONFIG['USER_MANAGER']['BURN_FILE'], 'w') as f:
|
||||
f.write(str(download_burnlist(CONFIG)))
|
||||
|
||||
|
||||
reactor.run()
|
||||
|
15
config.py
15
config.py
@ -107,6 +107,7 @@ def build_config(_config_file):
|
||||
CONFIG['REPORTS'] = {}
|
||||
CONFIG['LOGGER'] = {}
|
||||
CONFIG['ALIASES'] = {}
|
||||
CONFIG['USER_MANAGER'] = {}
|
||||
CONFIG['SYSTEMS'] = {}
|
||||
|
||||
try:
|
||||
@ -153,6 +154,19 @@ def build_config(_config_file):
|
||||
'STALE_TIME': config.getint(section, 'STALE_DAYS') * 86400,
|
||||
})
|
||||
|
||||
elif section == 'USER_MANAGER':
|
||||
CONFIG['USER_MANAGER'].update({
|
||||
'THIS_SERVER_NAME': config.get(section, 'THIS_SERVER_NAME'),
|
||||
'URL': config.get(section, 'URL'),
|
||||
'APPEND_INT': config.getint(section, 'APPEND_INT'),
|
||||
'SHARED_SECRET': config.get(section, 'SHARED_SECRET'),
|
||||
'SHORTEN_PASSPHRASE': config.getboolean(section, 'SHORTEN_PASSPHRASE'),
|
||||
'BURN_FILE': config.get(section, 'BURN_FILE'),
|
||||
'BURN_INT': config.getint(section, 'BURN_INT'),
|
||||
|
||||
|
||||
})
|
||||
|
||||
elif config.getboolean(section, 'ENABLED'):
|
||||
if config.get(section, 'MODE') == 'PEER':
|
||||
CONFIG['SYSTEMS'].update({section: {
|
||||
@ -249,6 +263,7 @@ def build_config(_config_file):
|
||||
CONFIG['SYSTEMS'].update({section: {
|
||||
'MODE': config.get(section, 'MODE'),
|
||||
'ENABLED': config.getboolean(section, 'ENABLED'),
|
||||
'USE_USER_MAN': config.getboolean(section, 'USE_USER_MAN'),
|
||||
'REPEAT': config.getboolean(section, 'REPEAT'),
|
||||
'MAX_PEERS': config.getint(section, 'MAX_PEERS'),
|
||||
'IP': gethostbyname(config.get(section, 'IP')),
|
||||
|
@ -126,7 +126,7 @@ STALE_DAYS: 7
|
||||
# Otherwise ACLs work as described in the global stanza
|
||||
[OBP-1]
|
||||
MODE: OPENBRIDGE
|
||||
ENABLED: True
|
||||
ENABLED: False
|
||||
IP:
|
||||
PORT: 62035
|
||||
NETWORK_ID: 3129100
|
||||
@ -138,6 +138,23 @@ USE_ACL: True
|
||||
SUB_ACL: DENY:1
|
||||
TGID_ACL: PERMIT:ALL
|
||||
|
||||
# USER MANAGER
|
||||
# This is where to configure the details for use with a user managment script
|
||||
[USER_MANAGER]
|
||||
THIS_SERVER_NAME: My MMDVM Server
|
||||
USE_USER_MAN: True
|
||||
# URL of the user managment server
|
||||
URL: http://localhost:8080/auth
|
||||
# Integer appended to DMR ID during the generation of a passphrase
|
||||
APPEND_INT: 1
|
||||
# Secret used to authenticate with user managment server, before checking if user login is approved
|
||||
SHARED_SECRET: test
|
||||
# Shorten passphrases to 8 characters
|
||||
SHORTEN_PASSPHRASE: False
|
||||
BURN_FILE: ./burn_ids.txt
|
||||
BURN_INT: 5
|
||||
|
||||
|
||||
# MASTER INSTANCES - DUPLICATE SECTION FOR MULTIPLE MASTERS
|
||||
# HomeBrew Protocol Master instances go here.
|
||||
# IP may be left blank if there's one interface on your system.
|
||||
@ -154,6 +171,10 @@ TGID_ACL: PERMIT:ALL
|
||||
[MASTER-1]
|
||||
MODE: MASTER
|
||||
ENABLED: True
|
||||
|
||||
# Use the user manager? If False, MASTER instance will operate as normal.
|
||||
USE_USER_MAN: False
|
||||
|
||||
REPEAT: True
|
||||
MAX_PEERS: 10
|
||||
EXPORT_AMBE: False
|
||||
@ -181,7 +202,7 @@ TGID_TS2_ACL: PERMIT:ALL
|
||||
# See comments in the GLOBAL stanza
|
||||
[REPEATER-1]
|
||||
MODE: PEER
|
||||
ENABLED: True
|
||||
ENABLED: False
|
||||
LOOSE: False
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
@ -213,7 +234,7 @@ TGID_TS2_ACL: PERMIT:ALL
|
||||
|
||||
[XLX-1]
|
||||
MODE: XLXPEER
|
||||
ENABLED: True
|
||||
ENABLED: False
|
||||
LOOSE: True
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
|
149
hblink.py
149
hblink.py
@ -55,6 +55,13 @@ from reporting_const import *
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Used for user auth
|
||||
import os, ast
|
||||
import requests, json
|
||||
import base64
|
||||
import libscrc
|
||||
|
||||
|
||||
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
@ -100,6 +107,23 @@ def acl_check(_id, _acl):
|
||||
return not _acl[0]
|
||||
|
||||
|
||||
def download_burnlist(_CONFIG):
|
||||
user_man_url = _CONFIG['USER_MANAGER']['URL']
|
||||
shared_secret = str(sha256(_CONFIG['USER_MANAGER']['SHARED_SECRET'].encode()).hexdigest())
|
||||
burn_check = {
|
||||
'burn_list':True,
|
||||
'secret':shared_secret
|
||||
}
|
||||
json_object = json.dumps(burn_check, indent = 4)
|
||||
try:
|
||||
req = requests.post(user_man_url, data=json_object, headers={'Content-Type': 'application/json'})
|
||||
resp = json.loads(req.text)
|
||||
return resp['burn_list']
|
||||
# For exception, write blank dict
|
||||
except requests.ConnectionError:
|
||||
return {}
|
||||
|
||||
|
||||
#************************************************
|
||||
# OPENBRIDGE CLASS
|
||||
#************************************************
|
||||
@ -230,6 +254,91 @@ class HBSYSTEM(DatagramProtocol):
|
||||
self.datagramReceived = self.peer_datagramReceived
|
||||
self.dereg = self.peer_dereg
|
||||
|
||||
def check_user_man(self, _id, server_name, peer_ip):
|
||||
#Change this to a config value
|
||||
user_man_url = self._CONFIG['USER_MANAGER']['URL']
|
||||
shared_secret = str(sha256(self._CONFIG['USER_MANAGER']['SHARED_SECRET'].encode()).hexdigest())
|
||||
#print(int(str(int_id(_id))[:7]))
|
||||
auth_check = {
|
||||
'secret':shared_secret,
|
||||
'login_id':int(str(int_id(_id))[:7]),
|
||||
'login_ip': peer_ip,
|
||||
'login_server': server_name
|
||||
}
|
||||
json_object = json.dumps(auth_check, indent = 4)
|
||||
try:
|
||||
req = requests.post(user_man_url, data=json_object, headers={'Content-Type': 'application/json'})
|
||||
resp = json.loads(req.text)
|
||||
return resp
|
||||
except requests.ConnectionError:
|
||||
return {'allow':True}
|
||||
|
||||
def send_login_conf(self, _id, server_name, peer_ip, old_auth):
|
||||
#Change this to a config value
|
||||
user_man_url = self._CONFIG['USER_MANAGER']['URL']
|
||||
shared_secret = str(sha256(self._CONFIG['USER_MANAGER']['SHARED_SECRET'].encode()).hexdigest())
|
||||
#print(int(str(int_id(_id))[:7]))
|
||||
auth_conf = {
|
||||
'secret':shared_secret,
|
||||
'login_id':int(str(int_id(_id))[:7]),
|
||||
'login_ip': peer_ip,
|
||||
'login_server': server_name,
|
||||
'login_confirmed': True,
|
||||
'old_auth': old_auth
|
||||
}
|
||||
json_object = json.dumps(auth_conf, indent = 4)
|
||||
try:
|
||||
req = requests.post(user_man_url, data=json_object, headers={'Content-Type': 'application/json'})
|
||||
# resp = json.loads(req.text)
|
||||
#return resp
|
||||
except Exception as e:
|
||||
logger.info(e)
|
||||
|
||||
def calc_passphrase(self, peer_id, _salt_str):
|
||||
burn_id = ast.literal_eval(os.popen('cat ' + self._CONFIG['USER_MANAGER']['BURN_FILE']).read())
|
||||
peer_id_trimmed = int(str(int_id(peer_id))[:7])
|
||||
try:
|
||||
print(self.ums_response)
|
||||
if self.ums_response['mode'] == 'legacy':
|
||||
_calc_hash = bhex(sha256(_salt_str+self._config['PASSPHRASE']).hexdigest())
|
||||
calc_passphrase = self._config['PASSPHRASE']
|
||||
if self.ums_response['mode'] == 'override':
|
||||
_calc_hash = bhex(sha256(_salt_str+str.encode(self.ums_response['value'])).hexdigest())
|
||||
if self.ums_response['mode'] == 'normal':
|
||||
_new_peer_id = bytes_4(int(str(int_id(peer_id))[:7]))
|
||||
peer_id_trimmed = str(peer_id_trimmed)
|
||||
try:
|
||||
if burn_id[peer_id_trimmed]:
|
||||
logger.info('User ID has been burned. Requiring passphrase version: ' + str(burn_id[peer_id_trimmed]))
|
||||
calc_passphrase = base64.b64encode(bytes.fromhex(str(hex(libscrc.ccitt((_new_peer_id) + burn_id[peer_id_trimmed].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['BURN_INT'].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big') + bytes.fromhex(str(hex(libscrc.posix((_new_peer_id) + burn_id[peer_id_trimmed].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['BURN_INT'].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big'))))[2:].zfill(8)))))[2:].zfill(4)) + (_new_peer_id) + burn_id[peer_id_trimmed].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['BURN_INT'].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big') + bytes.fromhex(str(hex(libscrc.posix((_new_peer_id) + burn_id[peer_id_trimmed].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['BURN_INT'].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big'))))[2:].zfill(8)))
|
||||
except:
|
||||
calc_passphrase = base64.b64encode(bytes.fromhex(str(hex(libscrc.ccitt((_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big') + bytes.fromhex(str(hex(libscrc.posix((_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big'))))[2:].zfill(8)))))[2:].zfill(4)) + (_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big') + bytes.fromhex(str(hex(libscrc.posix((_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big'))))[2:].zfill(8)))
|
||||
if self._CONFIG['USER_MANAGER']['SHORTEN_PASSPHRASE'] == True:
|
||||
calc_passphrase = calc_passphrase[-8:]
|
||||
if self._CONFIG['USER_MANAGER']['SHORTEN_PASSPHRASE'] == False:
|
||||
pass
|
||||
_calc_hash = bhex(sha256(_salt_str+calc_passphrase).hexdigest())
|
||||
#If exception, assume UMS down and default to calculated passphrase
|
||||
except Exception as e:
|
||||
logger.info('Execption, UMS possibly down')
|
||||
_new_peer_id = bytes_4(int(str(int_id(peer_id))[:7]))
|
||||
if peer_id_trimmed in burn_id:
|
||||
logger.info('User ID has been burned. Requiring passphrase version: ' + str(burn_id[peer_id_trimmed]))
|
||||
calc_passphrase = base64.b64encode(bytes.fromhex(str(hex(libscrc.ccitt((_new_peer_id) + burn_id[peer_id_trimmed].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['BURN_INT'].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big') + bytes.fromhex(str(hex(libscrc.posix((_new_peer_id) + burn_id[peer_id_trimmed].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['BURN_INT'].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big'))))[2:].zfill(8)))))[2:].zfill(4)) + (_new_peer_id) + burn_id[peer_id_trimmed].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['BURN_INT'].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big') + bytes.fromhex(str(hex(libscrc.posix((_new_peer_id) + burn_id[peer_id_trimmed].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['BURN_INT'].to_bytes(2, 'big') + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big'))))[2:].zfill(8)))
|
||||
else:
|
||||
calc_passphrase = base64.b64encode(bytes.fromhex(str(hex(libscrc.ccitt((_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big') + bytes.fromhex(str(hex(libscrc.posix((_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big'))))[2:].zfill(8)))))[2:].zfill(4)) + (_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big') + bytes.fromhex(str(hex(libscrc.posix((_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big'))))[2:].zfill(8)))
|
||||
|
||||
#calc_passphrase = base64.b64encode(bytes.fromhex(str(hex(libscrc.ccitt((_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big') + bytes.fromhex(str(hex(libscrc.posix((_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big'))))[2:].zfill(8)))))[2:].zfill(4)) + (_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big') + bytes.fromhex(str(hex(libscrc.posix((_new_peer_id) + self._CONFIG['USER_MANAGER']['APPEND_INT'].to_bytes(2, 'big'))))[2:].zfill(8)))
|
||||
if self._CONFIG['USER_MANAGER']['SHORTEN_PASSPHRASE'] == True:
|
||||
calc_passphrase = calc_passphrase[-8:]
|
||||
if self._CONFIG['USER_MANAGER']['SHORTEN_PASSPHRASE'] == False:
|
||||
pass
|
||||
_calc_hash = bhex(sha256(_salt_str+calc_passphrase).hexdigest())
|
||||
print(calc_passphrase)
|
||||
# print(_calc_hash)
|
||||
return _calc_hash
|
||||
|
||||
|
||||
def startProtocol(self):
|
||||
# Set up periodic loop for tracking pings from peers. Run every 'PING_TIME' seconds
|
||||
self._system_maintenance = task.LoopingCall(self.maintenance_loop)
|
||||
@ -326,6 +435,7 @@ class HBSYSTEM(DatagramProtocol):
|
||||
|
||||
# Aliased in __init__ to datagramReceived if system is a master
|
||||
def master_datagramReceived(self, _data, _sockaddr):
|
||||
global user_db
|
||||
# Keep This Line Commented Unless HEAVILY Debugging!
|
||||
# logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_data))
|
||||
|
||||
@ -405,7 +515,22 @@ class HBSYSTEM(DatagramProtocol):
|
||||
# Check to see if we've reached the maximum number of allowed peers
|
||||
if len(self._peers) < self._config['MAX_PEERS']:
|
||||
# Check for valid Radio ID
|
||||
#print(self.check_user_man(_peer_id))
|
||||
if self._config['USE_USER_MAN'] == True:
|
||||
self.ums_response = self.check_user_man(_peer_id, self._CONFIG['USER_MANAGER']['THIS_SERVER_NAME'], _sockaddr[0])
|
||||
## print(self.ums_response)
|
||||
#Will allow anyone to attempt authentication, used for a transition period
|
||||
## if acl_check(_peer_id, self._CONFIG['GLOBAL']['REG_ACL']) and self.ums_response['allow'] or acl_check(_peer_id, self._CONFIG['GLOBAL']['REG_ACL']) and acl_check(_peer_id, self._config['REG_ACL']):
|
||||
if acl_check(_peer_id, self._CONFIG['GLOBAL']['REG_ACL']) and self.ums_response['allow']:
|
||||
user_auth = self.ums_response['allow']
|
||||
else:
|
||||
user_auth = False
|
||||
print(user_auth)
|
||||
if self._config['USE_USER_MAN'] == False:
|
||||
# print('False')
|
||||
if acl_check(_peer_id, self._CONFIG['GLOBAL']['REG_ACL']) and acl_check(_peer_id, self._config['REG_ACL']):
|
||||
user_auth = True
|
||||
if user_auth == True:
|
||||
# Build the configuration data strcuture for the peer
|
||||
self._peers.update({_peer_id: {
|
||||
'CONNECTION': 'RPTL-RECEIVED',
|
||||
@ -437,6 +562,7 @@ class HBSYSTEM(DatagramProtocol):
|
||||
self.send_peer(_peer_id, b''.join([RPTACK, _salt_str]))
|
||||
self._peers[_peer_id]['CONNECTION'] = 'CHALLENGE_SENT'
|
||||
logger.info('(%s) Sent Challenge Response to %s for login: %s', self._system, int_id(_peer_id), self._peers[_peer_id]['SALT'])
|
||||
## print(self._peers)
|
||||
else:
|
||||
self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr)
|
||||
logger.warning('(%s) Invalid Login from %s Radio ID: %s Denied by Registation ACL', self._system, _sockaddr[0], int_id(_peer_id))
|
||||
@ -453,11 +579,27 @@ class HBSYSTEM(DatagramProtocol):
|
||||
_this_peer['LAST_PING'] = time()
|
||||
_sent_hash = _data[8:]
|
||||
_salt_str = bytes_4(_this_peer['SALT'])
|
||||
# Used to allow config passphrase AND calculated.
|
||||
_ocalc_hash = bhex(sha256(_salt_str+self._config['PASSPHRASE']).hexdigest())
|
||||
|
||||
#print(self.ums_response)
|
||||
if self._config['USE_USER_MAN'] == True:
|
||||
# print(self.calc_passphrase(_peer_id, _salt_str))
|
||||
_calc_hash = self.calc_passphrase(_peer_id, _salt_str)
|
||||
if self._config['USE_USER_MAN'] == False:
|
||||
_calc_hash = bhex(sha256(_salt_str+self._config['PASSPHRASE']).hexdigest())
|
||||
if _sent_hash == _calc_hash:
|
||||
# Uncomment below to only accept calculated passphrase
|
||||
# if _sent_hash == _calc_hash:
|
||||
# Condition below accepts either calculated passphrase or config passphrase
|
||||
if _sent_hash == _calc_hash or _sent_hash == _ocalc_hash:
|
||||
_this_peer['CONNECTION'] = 'WAITING_CONFIG'
|
||||
self.send_peer(_peer_id, b''.join([RPTACK, _peer_id]))
|
||||
logger.info('(%s) Peer %s has completed the login exchange successfully', self._system, _this_peer['RADIO_ID'])
|
||||
#self.send_login_conf(_peer_id, self._CONFIG['USER_MANAGER']['THIS_SERVER_NAME'], _sockaddr[0], False)
|
||||
if _sent_hash == _ocalc_hash:
|
||||
self.send_login_conf(_peer_id, self._CONFIG['USER_MANAGER']['THIS_SERVER_NAME'], _sockaddr[0], True)
|
||||
else:
|
||||
self.send_login_conf(_peer_id, self._CONFIG['USER_MANAGER']['THIS_SERVER_NAME'], _sockaddr[0], False)
|
||||
else:
|
||||
logger.info('(%s) Peer %s has FAILED the login exchange successfully', self._system, _this_peer['RADIO_ID'])
|
||||
self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr)
|
||||
@ -818,6 +960,7 @@ if __name__ == '__main__':
|
||||
|
||||
peer_ids, subscriber_ids, talkgroup_ids = mk_aliases(CONFIG)
|
||||
|
||||
|
||||
# INITIALIZE THE REPORTING LOOP
|
||||
if CONFIG['REPORTS']['REPORT']:
|
||||
report_server = config_reports(CONFIG, reportFactory)
|
||||
@ -836,4 +979,8 @@ if __name__ == '__main__':
|
||||
reactor.listenUDP(CONFIG['SYSTEMS'][system]['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['IP'])
|
||||
logger.debug('(GLOBAL) %s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system])
|
||||
|
||||
# Download burn list
|
||||
with open(CONFIG['USER_MANAGER']['BURN_FILE'], 'w') as f:
|
||||
f.write(str(download_burnlist(CONFIG)))
|
||||
|
||||
reactor.run()
|
||||
|
2987
user_managment/app.py
Normal file
2987
user_managment/app.py
Normal file
File diff suppressed because it is too large
Load Diff
62
user_managment/config-SAMPLE.py
Normal file
62
user_managment/config-SAMPLE.py
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
'''
|
||||
Settings for user management portal.
|
||||
'''
|
||||
# Database location
|
||||
db_location = 'sqlite:///./users.db'
|
||||
|
||||
# Legacy passphrase used in hblink.cfg
|
||||
legacy_passphrase = 'passw0rd'
|
||||
|
||||
# Trim passphrases to 8 characters
|
||||
use_short_passphrase = False
|
||||
|
||||
# Title of the Dashboard
|
||||
title = 'MMDVM User Portal'
|
||||
# Port to run server
|
||||
ums_port = 8080
|
||||
# IP to run server on
|
||||
ums_host = '127.0.0.1'
|
||||
|
||||
url = 'http://localhost:8080'
|
||||
|
||||
append_int = 1
|
||||
|
||||
shared_secrets = ['test']
|
||||
|
||||
|
||||
burn_int = 5
|
||||
|
||||
legacy_passphrase = 'passw0rd'
|
||||
|
||||
# Email settings
|
||||
MAIL_SERVER = 'smtp.gmail.com'
|
||||
MAIL_PORT = 465
|
||||
MAIL_USE_SSL = True
|
||||
MAIL_USE_TLS = False
|
||||
MAIL_USERNAME = 'app@gmail.com'
|
||||
MAIL_PASSWORD = 'password'
|
||||
MAIL_DEFAULT_SENDER = '"' + title + '" <app@gmail.com>'
|
||||
|
||||
# UMS settings
|
||||
secret_key = 'SUPER SECRET LONG KEY'
|
||||
|
||||
USER_ENABLE_EMAIL = True
|
||||
USER_ENABLE_USERNAME = True # Enable username authentication
|
||||
USER_REQUIRE_RETYPE_PASSWORD = True # Simplify register form
|
||||
USER_ENABLE_CHANGE_USERNAME = False
|
||||
USER_ENABLE_MULTIPLE_EMAILS = True
|
||||
USER_ENABLE_CONFIRM_EMAIL = True
|
||||
USER_ENABLE_REGISTER = True
|
||||
USER_AUTO_LOGIN_AFTER_CONFIRM = False
|
||||
USER_SHOW_USERNAME_DOES_NOT_EXIST = True
|
||||
|
||||
# Gateway contact info displayed on about page.
|
||||
contact_name = 'your name'
|
||||
contact_call = 'N0CALL'
|
||||
contact_email = 'email@example.org'
|
||||
contact_website = 'https://hbl.ink'
|
||||
|
||||
# Time format for display
|
||||
time_format = '%H:%M:%S - %m/%d/%y'
|
||||
|
5
user_managment/gen_script_template-SAMPLE.py
Normal file
5
user_managment/gen_script_template-SAMPLE.py
Normal file
@ -0,0 +1,5 @@
|
||||
def gen_script(dmr_id, passphrase):
|
||||
script = '''
|
||||
DMR ID: ''' + str(dmr_id) + ''' \n Passphrase: ''' + str(passphrase) + '''
|
||||
'''
|
||||
return script
|
BIN
user_managment/static/HBlink.png
Normal file
BIN
user_managment/static/HBlink.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
30
user_managment/templates/flask_user/edit_user_profile.html
Normal file
30
user_managment/templates/flask_user/edit_user_profile.html
Normal file
@ -0,0 +1,30 @@
|
||||
{% extends 'flask_user/_authorized_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_checkbox_field, render_submit_field %}
|
||||
<h1>{%trans%}User profile{%endtrans%}</h1>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{% for field in form %}
|
||||
{% if not field.flags.hidden %}
|
||||
{% if field.type=='SubmitField' %}
|
||||
{{ render_submit_field(field, tabindex=loop.index*10) }}
|
||||
{% else %}
|
||||
{{ render_field(field, tabindex=loop.index*10) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</form>
|
||||
<br/>
|
||||
<p><a href="../update_ids"><strong>Update your information from RadioID.net</strong></a></p>
|
||||
{% if not user_manager.USER_ENABLE_AUTH0 %}
|
||||
{% if user_manager.USER_ENABLE_CHANGE_USERNAME %}
|
||||
<p><a href="{{ url_for('user.change_username') }}">{%trans%}Change username{%endtrans%}</a></p>
|
||||
{% endif %}
|
||||
{% if user_manager.USER_ENABLE_CHANGE_PASSWORD %}
|
||||
<p><a href="{{ url_for('user.change_password') }}">{%trans%}Change password{%endtrans%}</a></p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,8 @@
|
||||
<p>Dear {{ user.email }} - {{ user.username }},</p>
|
||||
|
||||
{% block message %}
|
||||
{% endblock %}
|
||||
|
||||
<p>Sincerely,<br/>
|
||||
{{ app_name }}
|
||||
</p>
|
69
user_managment/templates/flask_user/login.html
Normal file
69
user_managment/templates/flask_user/login.html
Normal file
@ -0,0 +1,69 @@
|
||||
{% extends 'flask_user/_public_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_checkbox_field, render_submit_field %}
|
||||
<h1>{%trans%}Sign in{%endtrans%}</h1>
|
||||
<p> </p>
|
||||
|
||||
<strong>Your username MUST be your callsign or email address.</strong>
|
||||
<p> </p>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{# Username or Email field #}
|
||||
{% set field = form.username if user_manager.USER_ENABLE_USERNAME else form.email %}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
||||
{# Label on left, "New here? Register." on right #}
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<label for="{{ field.id }}" class="control-label">{{ field.label.text }}</label>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
{% if user_manager.USER_ENABLE_REGISTER and not user_manager.USER_REQUIRE_INVITATION %}
|
||||
<a href="{{ url_for('user.register') }}" tabindex='190'>
|
||||
{%trans%}New here? Register.{%endtrans%}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{{ field(class_='form-control', tabindex=110) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Password field #}
|
||||
{% set field = form.password %}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
||||
{# Label on left, "Forgot your Password?" on right #}
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<label for="{{ field.id }}" class="control-label">{{ field.label.text }}</label>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
{% if user_manager.USER_ENABLE_FORGOT_PASSWORD %}
|
||||
<a href="{{ url_for('user.forgot_password') }}" tabindex='195'>
|
||||
{%trans%}Forgot your Password?{%endtrans%}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{{ field(class_='form-control', tabindex=120) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Remember me #}
|
||||
{% if user_manager.USER_ENABLE_REMEMBER_ME %}
|
||||
{{ render_checkbox_field(login_form.remember_me, tabindex=130) }}
|
||||
{% endif %}
|
||||
|
||||
{# Submit button #}
|
||||
{{ render_submit_field(form.submit, tabindex=180) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
50
user_managment/templates/flask_user/register.html
Normal file
50
user_managment/templates/flask_user/register.html
Normal file
@ -0,0 +1,50 @@
|
||||
{% extends 'flask_user/_public_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
|
||||
<h1>{%trans%}Register{%endtrans%}</h1>
|
||||
<p> </p>
|
||||
|
||||
<strong>Your username MUST be your callsign.</strong> After filling out the fields, a confirmation link will be emailed to you.
|
||||
<p> </p>
|
||||
|
||||
<form action="" method="POST" novalidate formnovalidate class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{# Username or Email #}
|
||||
{% set field = form.username if user_manager.USER_ENABLE_USERNAME else form.email %}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
||||
{# Label on left, "Already registered? Sign in." on right #}
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<label for="{{ field.id }}" class="control-label">{{ field.label.text }}</label>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
{% if user_manager.USER_ENABLE_REGISTER %}
|
||||
<a href="{{ url_for('user.login') }}" tabindex='290'>
|
||||
{%trans%}Already registered? Sign in.{%endtrans%}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{{ field(class_='form-control', tabindex=210) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if user_manager.USER_ENABLE_EMAIL and user_manager.USER_ENABLE_USERNAME %}
|
||||
{{ render_field(form.email, tabindex=220) }}
|
||||
{% endif %}
|
||||
|
||||
{{ render_field(form.password, tabindex=230) }}
|
||||
|
||||
{% if user_manager.USER_REQUIRE_RETYPE_PASSWORD %}
|
||||
{{ render_field(form.retype_password, tabindex=240) }}
|
||||
{% endif %}
|
||||
|
||||
{{ render_submit_field(form.submit, tabindex=280) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
147
user_managment/templates/flask_user_layout.html
Normal file
147
user_managment/templates/flask_user_layout.html
Normal file
@ -0,0 +1,147 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ user_manager.USER_APP_NAME }}</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- In-lining styles to avoid needing a separate .css file -->
|
||||
<style>
|
||||
hr { border-color: #cccccc; margin: 0; }
|
||||
.no-margins { margin: 0px; }
|
||||
.with-margins { margin: 10px; }
|
||||
.col-centered { float: none; margin: 0 auto; }
|
||||
</style>
|
||||
|
||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7/html5shiv.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
{# *** Allow sub-templates to insert extra html to the head section *** #}
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<h1 style="text-align: center;">{{ user_manager.USER_APP_NAME }}</h1>
|
||||
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="{{ url_for('static', filename='HBlink.png') }}" alt="Logo" width="300" height="144" /></p>
|
||||
<h1 style="text-align: center;">{{title}}</h1>
|
||||
<hr />
|
||||
<table style="width: 700px; margin-left: auto; margin-right: auto;" border="black" cellspacing="3" cellpadding="3">
|
||||
<tbody>
|
||||
<tr>
|
||||
|
||||
<td style="text-align: center;"><a href={{url}}/>Home</a></td>
|
||||
{% if not call_or_get(current_user.is_authenticated) %}
|
||||
<td style="text-align: center;"><a href={{ url_for('user.register') }}>Register</a></td>
|
||||
<td style="text-align: center;"><a href={{ url_for('user.login') }}>Sign in</a></td>
|
||||
{% endif %}
|
||||
{% if call_or_get(current_user.is_authenticated) %}
|
||||
{% if call_or_get(current_user.has_roles('Admin')) %}
|
||||
<td style="text-align: center;"><a href={{url}}/add_user><strong>Add a User</strong></a></td>
|
||||
<td style="text-align: center;"><a href={{url}}/list_users><strong>Edit Users</strong></a></td>
|
||||
<td style="text-align: center;"><a href={{url}}/approve_users><strong>Waiting Approval</strong></a></td>
|
||||
<td style="text-align: center;"><a href={{url}}/auth_log><strong>Auth Log</strong></a></td>
|
||||
|
||||
{% endif %}
|
||||
<td style="text-align: center;"><a href={{url}}/help>Help</a></td>
|
||||
<td style="text-align: center;"><a href={{url}}/generate_passphrase>View Passphrase(s)</a></td>
|
||||
<td style="text-align: center;"><a href="{{ url_for('user.edit_user_profile') }}">Edit {{ current_user.username or current_user.email }}</a></td>
|
||||
<td style="text-align: center;"><a href={{ url_for('user.logout') }}>Sign out</a></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% if call_or_get(current_user.is_authenticated) %}
|
||||
{% if call_or_get(current_user.has_roles('Admin')) %}
|
||||
<table style="width: 700px; margin-left: auto; margin-right: auto;" border="black" cellspacing="3" cellpadding="3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="text-align: center;"><a href={{url}}/edit_server>Manage Server Configs</a></td>
|
||||
<td style="text-align: center;"><a href={{url}}/edit_peer>Manage Peers</a></td>
|
||||
<td style="text-align: center;"><a href={{url}}/edit_peer>Manage Masters Configs</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<hr />
|
||||
{% block body %}
|
||||
<!--
|
||||
<div id="header-div" class="clearfix with-margins">
|
||||
<div class="pull-left"><a href="/"><h1 class="no-margins">{{ user_manager.USER_APP_NAME }}</h1></a></div>
|
||||
<div class="pull-right">
|
||||
{% if call_or_get(current_user.is_authenticated) %}
|
||||
<a href="{{ url_for('user.edit_user_profile') }}">{{ current_user.username or current_user.email }}</a>
|
||||
|
|
||||
<a href="{{ url_for('user.logout') }}">{%trans%}Sign out{%endtrans%}</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('user.login') }}">{%trans%}Sign in{%endtrans%}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% block menu %}
|
||||
<div id="menu-div" class="with-margins">
|
||||
<a href="/">{%trans%}Home page{%endtrans%}</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
-->
|
||||
<hr class="no-margins"/>
|
||||
|
||||
<div id="main-div" class="with-margins">
|
||||
{# One-time system messages called Flash messages #}
|
||||
{% block flash_messages %}
|
||||
{%- with messages = get_flashed_messages(with_categories=true) -%}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
{% if category=='error' %}
|
||||
{% set category='danger' %}
|
||||
{% endif %}
|
||||
<div class="alert alert-{{category}}">{{ message|safe }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{%- endwith %}
|
||||
{% endblock %}
|
||||
{% block main %}
|
||||
{% block content %}
|
||||
|
||||
{{markup_content}}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<hr class="no-margins"/>
|
||||
<div id="footer-div" class="clearfix with-margins">
|
||||
<p style="text-align: center;"><strong>{{ user_manager.USER_APP_NAME }}<br /></strong><a href="https://github.com/kf7eel/hblink3">HBNet DMR Server</a><br />Created by KF7EEL<br />Contributors: W7NCX, N9VW, KC7AAD, NO7RF</p>
|
||||
<!--
|
||||
|
||||
<div class="pull-left">{{ user_manager.USER_APP_NAME }}</div>
|
||||
<div class="pull-right">Credits: KF7EEL, W7NCX, N9VW, KC7AAD, NO7RF</div>
|
||||
-->
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<!-- Bootstrap -->
|
||||
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
|
||||
|
||||
{# *** Allow sub-templates to insert extra html to the bottom of the body *** #}
|
||||
{% block extra_js %}{% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
4
user_managment/templates/help.html
Normal file
4
user_managment/templates/help.html
Normal file
@ -0,0 +1,4 @@
|
||||
{% extends 'flask_user/_public_base.html' %}
|
||||
{% block content %}
|
||||
This is a help page.</p>
|
||||
{% endblock %}
|
4
user_managment/templates/index.html
Normal file
4
user_managment/templates/index.html
Normal file
@ -0,0 +1,4 @@
|
||||
{% extends 'flask_user/_public_base.html' %}
|
||||
{% block content %}
|
||||
<p>Welcome to the <strong>{{ user_manager.USER_APP_NAME }}</strong>. This tool is used to manage your access.</p>
|
||||
{% endblock %}
|
34
user_managment/templates/view_passphrase.html
Normal file
34
user_managment/templates/view_passphrase.html
Normal file
@ -0,0 +1,34 @@
|
||||
{% extends 'flask_user/_public_base.html' %}
|
||||
{% block content %}
|
||||
<p> </p><h4 style="text-align: center;"><a href="/generate_passphrase/pi-star">Click here</a> for automated Pi-Star script.</h4><p> </p>
|
||||
<table style="width: 900px; margin-left: auto; margin-right: auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
<table style="width: 218px;" border="1">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 53.8333px;">Name:</td>
|
||||
<td style="width: 147.167px; text-align: center;"><strong>My Server</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 53.8333px;">Host/IP:</td>
|
||||
<td style="width: 147.167px; text-align: center;"><strong>127.0.0.1</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 53.8333px;">Port:</td>
|
||||
<td style="width: 147.167px; text-align: center;"><strong>62030</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
<td>{{markup_content}}
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p> </p>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user