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 ###
|
### 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
|
from twisted.internet import reactor, task
|
||||||
|
|
||||||
# Things we import from the main hblink module
|
# 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.utils import bytes_3, int_id, get_alias
|
||||||
from dmr_utils3 import decode, bptc, const
|
from dmr_utils3 import decode, bptc, const
|
||||||
import config
|
import config
|
||||||
import log
|
import log
|
||||||
from const import *
|
from const import *
|
||||||
|
from hashlib import sha256
|
||||||
|
|
||||||
# Stuff for socket reporting
|
# Stuff for socket reporting
|
||||||
import pickle
|
import pickle
|
||||||
@ -55,8 +56,8 @@ import pickle
|
|||||||
# The module needs logging, but handlers, etc. are controlled by the parent
|
# The module needs logging, but handlers, etc. are controlled by the parent
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
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.
|
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||||
__copyright__ = 'Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group'
|
__copyright__ = 'Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||||
@ -65,6 +66,86 @@ __license__ = 'GNU GPLv3'
|
|||||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||||
__email__ = 'n0mjs@me.com'
|
__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
|
# Module gobal varaibles
|
||||||
|
|
||||||
# Dictionary for dynamically mapping unit (subscriber) to a system.
|
# 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'
|
cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/hblink.cfg'
|
||||||
|
|
||||||
# Call the external routine to build the configuration dictionary
|
# 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)
|
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)
|
# 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:
|
if not cli_args.RULES_FILE:
|
||||||
cli_args.RULES_FILE = os.path.dirname(os.path.abspath(__file__))+'/rules.py'
|
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 = stream_trimmer_task.start(5)
|
||||||
stream_trimmer.addErrback(loopingErrHandle)
|
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()
|
reactor.run()
|
||||||
|
15
config.py
15
config.py
@ -107,6 +107,7 @@ def build_config(_config_file):
|
|||||||
CONFIG['REPORTS'] = {}
|
CONFIG['REPORTS'] = {}
|
||||||
CONFIG['LOGGER'] = {}
|
CONFIG['LOGGER'] = {}
|
||||||
CONFIG['ALIASES'] = {}
|
CONFIG['ALIASES'] = {}
|
||||||
|
CONFIG['USER_MANAGER'] = {}
|
||||||
CONFIG['SYSTEMS'] = {}
|
CONFIG['SYSTEMS'] = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -153,6 +154,19 @@ def build_config(_config_file):
|
|||||||
'STALE_TIME': config.getint(section, 'STALE_DAYS') * 86400,
|
'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'):
|
elif config.getboolean(section, 'ENABLED'):
|
||||||
if config.get(section, 'MODE') == 'PEER':
|
if config.get(section, 'MODE') == 'PEER':
|
||||||
CONFIG['SYSTEMS'].update({section: {
|
CONFIG['SYSTEMS'].update({section: {
|
||||||
@ -249,6 +263,7 @@ def build_config(_config_file):
|
|||||||
CONFIG['SYSTEMS'].update({section: {
|
CONFIG['SYSTEMS'].update({section: {
|
||||||
'MODE': config.get(section, 'MODE'),
|
'MODE': config.get(section, 'MODE'),
|
||||||
'ENABLED': config.getboolean(section, 'ENABLED'),
|
'ENABLED': config.getboolean(section, 'ENABLED'),
|
||||||
|
'USE_USER_MAN': config.getboolean(section, 'USE_USER_MAN'),
|
||||||
'REPEAT': config.getboolean(section, 'REPEAT'),
|
'REPEAT': config.getboolean(section, 'REPEAT'),
|
||||||
'MAX_PEERS': config.getint(section, 'MAX_PEERS'),
|
'MAX_PEERS': config.getint(section, 'MAX_PEERS'),
|
||||||
'IP': gethostbyname(config.get(section, 'IP')),
|
'IP': gethostbyname(config.get(section, 'IP')),
|
||||||
|
@ -126,7 +126,7 @@ STALE_DAYS: 7
|
|||||||
# Otherwise ACLs work as described in the global stanza
|
# Otherwise ACLs work as described in the global stanza
|
||||||
[OBP-1]
|
[OBP-1]
|
||||||
MODE: OPENBRIDGE
|
MODE: OPENBRIDGE
|
||||||
ENABLED: True
|
ENABLED: False
|
||||||
IP:
|
IP:
|
||||||
PORT: 62035
|
PORT: 62035
|
||||||
NETWORK_ID: 3129100
|
NETWORK_ID: 3129100
|
||||||
@ -138,6 +138,23 @@ USE_ACL: True
|
|||||||
SUB_ACL: DENY:1
|
SUB_ACL: DENY:1
|
||||||
TGID_ACL: PERMIT:ALL
|
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
|
# MASTER INSTANCES - DUPLICATE SECTION FOR MULTIPLE MASTERS
|
||||||
# HomeBrew Protocol Master instances go here.
|
# HomeBrew Protocol Master instances go here.
|
||||||
# IP may be left blank if there's one interface on your system.
|
# IP may be left blank if there's one interface on your system.
|
||||||
@ -154,6 +171,10 @@ TGID_ACL: PERMIT:ALL
|
|||||||
[MASTER-1]
|
[MASTER-1]
|
||||||
MODE: MASTER
|
MODE: MASTER
|
||||||
ENABLED: True
|
ENABLED: True
|
||||||
|
|
||||||
|
# Use the user manager? If False, MASTER instance will operate as normal.
|
||||||
|
USE_USER_MAN: False
|
||||||
|
|
||||||
REPEAT: True
|
REPEAT: True
|
||||||
MAX_PEERS: 10
|
MAX_PEERS: 10
|
||||||
EXPORT_AMBE: False
|
EXPORT_AMBE: False
|
||||||
@ -181,7 +202,7 @@ TGID_TS2_ACL: PERMIT:ALL
|
|||||||
# See comments in the GLOBAL stanza
|
# See comments in the GLOBAL stanza
|
||||||
[REPEATER-1]
|
[REPEATER-1]
|
||||||
MODE: PEER
|
MODE: PEER
|
||||||
ENABLED: True
|
ENABLED: False
|
||||||
LOOSE: False
|
LOOSE: False
|
||||||
EXPORT_AMBE: False
|
EXPORT_AMBE: False
|
||||||
IP:
|
IP:
|
||||||
@ -213,7 +234,7 @@ TGID_TS2_ACL: PERMIT:ALL
|
|||||||
|
|
||||||
[XLX-1]
|
[XLX-1]
|
||||||
MODE: XLXPEER
|
MODE: XLXPEER
|
||||||
ENABLED: True
|
ENABLED: False
|
||||||
LOOSE: True
|
LOOSE: True
|
||||||
EXPORT_AMBE: False
|
EXPORT_AMBE: False
|
||||||
IP:
|
IP:
|
||||||
|
149
hblink.py
149
hblink.py
@ -55,6 +55,13 @@ from reporting_const import *
|
|||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
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.
|
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||||
__copyright__ = 'Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group'
|
__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]
|
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
|
# OPENBRIDGE CLASS
|
||||||
#************************************************
|
#************************************************
|
||||||
@ -230,6 +254,91 @@ class HBSYSTEM(DatagramProtocol):
|
|||||||
self.datagramReceived = self.peer_datagramReceived
|
self.datagramReceived = self.peer_datagramReceived
|
||||||
self.dereg = self.peer_dereg
|
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):
|
def startProtocol(self):
|
||||||
# Set up periodic loop for tracking pings from peers. Run every 'PING_TIME' seconds
|
# Set up periodic loop for tracking pings from peers. Run every 'PING_TIME' seconds
|
||||||
self._system_maintenance = task.LoopingCall(self.maintenance_loop)
|
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
|
# Aliased in __init__ to datagramReceived if system is a master
|
||||||
def master_datagramReceived(self, _data, _sockaddr):
|
def master_datagramReceived(self, _data, _sockaddr):
|
||||||
|
global user_db
|
||||||
# Keep This Line Commented Unless HEAVILY Debugging!
|
# Keep This Line Commented Unless HEAVILY Debugging!
|
||||||
# logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_data))
|
# 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
|
# Check to see if we've reached the maximum number of allowed peers
|
||||||
if len(self._peers) < self._config['MAX_PEERS']:
|
if len(self._peers) < self._config['MAX_PEERS']:
|
||||||
# Check for valid Radio ID
|
# 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']):
|
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
|
# Build the configuration data strcuture for the peer
|
||||||
self._peers.update({_peer_id: {
|
self._peers.update({_peer_id: {
|
||||||
'CONNECTION': 'RPTL-RECEIVED',
|
'CONNECTION': 'RPTL-RECEIVED',
|
||||||
@ -437,6 +562,7 @@ class HBSYSTEM(DatagramProtocol):
|
|||||||
self.send_peer(_peer_id, b''.join([RPTACK, _salt_str]))
|
self.send_peer(_peer_id, b''.join([RPTACK, _salt_str]))
|
||||||
self._peers[_peer_id]['CONNECTION'] = 'CHALLENGE_SENT'
|
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'])
|
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:
|
else:
|
||||||
self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr)
|
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))
|
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()
|
_this_peer['LAST_PING'] = time()
|
||||||
_sent_hash = _data[8:]
|
_sent_hash = _data[8:]
|
||||||
_salt_str = bytes_4(_this_peer['SALT'])
|
_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())
|
_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'
|
_this_peer['CONNECTION'] = 'WAITING_CONFIG'
|
||||||
self.send_peer(_peer_id, b''.join([RPTACK, _peer_id]))
|
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'])
|
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:
|
else:
|
||||||
logger.info('(%s) Peer %s has FAILED the login exchange successfully', self._system, _this_peer['RADIO_ID'])
|
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)
|
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)
|
peer_ids, subscriber_ids, talkgroup_ids = mk_aliases(CONFIG)
|
||||||
|
|
||||||
|
|
||||||
# INITIALIZE THE REPORTING LOOP
|
# INITIALIZE THE REPORTING LOOP
|
||||||
if CONFIG['REPORTS']['REPORT']:
|
if CONFIG['REPORTS']['REPORT']:
|
||||||
report_server = config_reports(CONFIG, reportFactory)
|
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'])
|
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])
|
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()
|
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