diff --git a/config.py b/config.py index ca9b345..4cfcac3 100755 --- a/config.py +++ b/config.py @@ -156,6 +156,7 @@ def build_config(_config_file): 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'), diff --git a/hblink.py b/hblink.py index 9b43c1e..74666e9 100755 --- a/hblink.py +++ b/hblink.py @@ -254,14 +254,16 @@ class HBSYSTEM(DatagramProtocol): self.datagramReceived = self.peer_datagramReceived self.dereg = self.peer_dereg - def check_user_man(self, _id): + 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 = self._CONFIG['USER_MANAGER']['SHARED_SECRET'] #print(int(str(int_id(_id))[:7])) auth_check = { + 'secret':shared_secret, 'login_id':int(str(int_id(_id))[:7]), - 'secret':shared_secret + 'login_ip': peer_ip, + 'login_server': server_name } json_object = json.dumps(auth_check, indent = 4) try: @@ -271,6 +273,27 @@ class HBSYSTEM(DatagramProtocol): 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 = self._CONFIG['USER_MANAGER']['SHARED_SECRET'] + #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 requests.ConnectionError: + # return {'allow':True} + 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]) @@ -416,9 +439,6 @@ class HBSYSTEM(DatagramProtocol): # Keep This Line Commented Unless HEAVILY Debugging! # logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_data)) - # Place holder for DB function - user_db = ast.literal_eval(os.popen('cat ./db.txt').read()) - # Extract the command, which is various length, all but one 4 significant characters -- RPTCL _command = _data[:4] @@ -497,7 +517,7 @@ class HBSYSTEM(DatagramProtocol): # 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.ums_response = self.check_user_man(_peer_id, self._CONFIG['USER_MANAGER']['THIS_SERVER_NAME'], _sockaddr[0]) ## print(self.ums_response) if acl_check(_peer_id, self._CONFIG['GLOBAL']['REG_ACL']) and self.ums_response['allow']: user_auth = self.ums_response['allow'] @@ -540,6 +560,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)) @@ -572,6 +593,11 @@ class HBSYSTEM(DatagramProtocol): _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) diff --git a/user_managment/app.py b/user_managment/app.py index 9bbbda4..abe9435 100644 --- a/user_managment/app.py +++ b/user_managment/app.py @@ -59,7 +59,7 @@ def get_ids(callsign): except: city = result['results'][0]['country'] for i in result['results']: - id_list[i['id']] = '' + id_list[i['id']] = 0 return str([id_list, f_name, l_name, city]) except: return str([{}, '', '', '']) @@ -146,6 +146,7 @@ def create_app(): last_name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='') dmr_ids = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='') city = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='') + notes = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='') #Used for initial approval initial_admin_approved = db.Column('initial_admin_approved', db.Boolean(), nullable=False, server_default='1') # Define the relationship to Role via UserRoles @@ -167,6 +168,17 @@ def create_app(): __tablename__ = 'burn_list' dmr_id = db.Column(db.Integer(), unique=True, primary_key=True) version = db.Column(db.Integer(), primary_key=True) + class AuthLog(db.Model): + __tablename__ = 'auth_log' + login_dmr_id = db.Column(db.Integer(), primary_key=True) + login_time = db.Column(db.DateTime(), primary_key=True) + peer_ip = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='') + server_name = db.Column(db.Integer(), primary_key=True) + login_auth_method = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='') + portal_username = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='') + login_type = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='') + + # Customize Flask-User class CustomUserManager(UserManager): @@ -236,7 +248,8 @@ def create_app(): email='admin@no.reply', email_confirmed_at=datetime.datetime.utcnow(), password=user_manager.hash_password('admin'), - initial_admin_approved = True + initial_admin_approved = True, + notes='Default admin account created during installation.' ) user.roles.append(Role(name='Admin')) user.roles.append(Role(name='User')) @@ -481,37 +494,37 @@ def create_app(): - @app.route('/mmdvm_log', methods=['POST', 'GET']) - @login_required # User must be authenticated - @roles_required('Admin') - def mmdvm_auth_list(): - display_number = 200 - content = ''' -

Last ''' + str(display_number) + ''' logins or attempts.

- - - - - - - - \n''' - mmdvm_logins.reverse() - for i in mmdvm_logins: - print(i) - if display_number == 0: - break - else: - content = content + ''' - - - - - ''' + '\n' - display_number = display_number - 1 - mmdvm_logins.reverse() - content = content + '
UserDMR IDAuthenticationTime
''' + str(i[1]) + '''''' + str(i[0]) + '''Value: ''' + str(i[2]) + '''\n
DB: ''' + str(i[3]) + '''
''' + datetime.datetime.fromtimestamp(i[4]).strftime(time_format) + '''
' - return render_template('flask_user_layout.html', markup_content = Markup(content)) +## @app.route('/mmdvm_log', methods=['POST', 'GET']) +## @login_required # User must be authenticated +## @roles_required('Admin') +## def mmdvm_auth_list(): +## display_number = 200 +## content = ''' +##

Last ''' + str(display_number) + ''' logins or attempts.

+## +## +## +## +## +## +## +## \n''' +## mmdvm_logins.reverse() +## for i in mmdvm_logins: +## print(i) +## if display_number == 0: +## break +## else: +## content = content + ''' +## +## +## +## +## ''' + '\n' +## display_number = display_number - 1 +## mmdvm_logins.reverse() +## content = content + '
UserDMR IDAuthenticationTime
''' + str(i[1]) + '''''' + str(i[0]) + '''Value: ''' + str(i[2]) + '''\n
DB: ''' + str(i[3]) + '''
''' + datetime.datetime.fromtimestamp(i[4]).strftime(time_format) + '''
' +## return render_template('flask_user_layout.html', markup_content = Markup(content)) @@ -640,6 +653,9 @@ def create_app(): if request.form.get('email') != edit_user.email: edit_user.email = request.form.get('email') content = content + '''

Changed email for user: ''' + str(user) + ''' to ''' + request.form.get('email') + '''

\n''' + if request.form.get('notes') != edit_user.notes: + edit_user.notes = request.form.get('notes') + content = content + '''

Changed notes for user: ''' + str(user) + '''.

\n''' if request.form.get('password') != '': edit_user.password = user_manager.hash_password(request.form.get('password')) content = content + '''

Changed password for user: ''' + str(user) + '''

\n''' @@ -757,6 +773,8 @@ def create_app():

Send user an email

+

View auth log for: ''' + u.username + '''

+
@@ -799,6 +817,12 @@ def create_app():
+ + + + @@ -902,6 +926,111 @@ def create_app(): except: return [False] + @app.route('/auth_log', methods=['POST', 'GET']) + @login_required # User must be authenticated + @roles_required('Admin') + def all_auth_list(): + if request.args.get('flush_db') == 'true': + content = '''

Flushed entire auth DB.

\n''' + authlog_flush() + elif request.args.get('portal_username'): + a = AuthLog.query.filter_by(portal_username=request.args.get('portal_username')).all() + content = ''' +

 

+ + +
+


+
+ + + + + + + + + + \n''' + for i in a: + content = content + ''' + + + + + + + + + + + +''' + content = content + '
+

DMR ID

+
+

Portal Username

+
+

Login IP

+
+

Calculated Passphrase

+
+

Server

+
+

Time (UTC)

+
+

Login Type

+
''' + str(i.login_dmr_id) + '''''' + i.portal_username + '''''' + i.peer_ip + '''''' + i.login_auth_method + '''''' + i.server_name + '''''' + str(i.login_time) + '''''' + str(i.login_type) + '''
' + else: + a = AuthLog.query.all() + content = ''' +

 

+

Flush entire auth log

+

 

+ + + + + + + + + + + + + \n''' + for i in a: + content = content + ''' + + + + + + + + + + + + ''' + + content = content + '
+

DMR ID

+
+

Portal Username

+
+

Login IP

+
+

Calculated Passphrase

+
+

Server

+
+

Time (UTC)

+
+

Login Type

+
''' + str(i.login_dmr_id) + '''''' + i.portal_username + '''''' + i.peer_ip + '''''' + i.login_auth_method + '''''' + i.server_name + '''''' + str(i.login_time) + '''''' + str(i.login_type) + '''
' + return render_template('flask_user_layout.html', markup_content = Markup(content)) + + @app.route('/test') def test_peer(): #user = User( @@ -995,18 +1124,20 @@ def create_app(): ## db.session.commit() ## #generate dict - b = BurnList.query.all() - print(b) - burn_dict = {} - for i in b: - print(i.dmr_id) - burn_dict[i.dmr_id] = i.version - content = burn_dict - # delete -## delete_b = BurnList.query.filter_by(dmr_id=3153591).first() -## db.session.delete(delete_b) -## db.session.commit() - +## b = BurnList.query.all() +## print(b) +## burn_dict = {} +## for i in b: +## print(i.dmr_id) +## burn_dict[i.dmr_id] = i.version +## content = burn_dict +## # delete +#### delete_b = BurnList.query.filter_by(dmr_id=3153591).first() +#### db.session.delete(delete_b) +#### db.session.commit() +## a = AuthLog.query.all() +## print(a) +## authlog_flush() return render_template('flask_user_layout.html', markup_content = Markup(content)) def get_burnlist(): @@ -1034,6 +1165,23 @@ def create_app(): delete_b = BurnList.query.filter_by(dmr_id=_dmr_id).first() db.session.delete(delete_b) db.session.commit() + + def authlog_add(_dmr_id, _peer_ip, _server_name, _portal_username, _auth_method, _login_type): + auth_log_add = AuthLog( + login_dmr_id=_dmr_id, + login_time=datetime.datetime.utcnow(), + portal_username = _portal_username, + peer_ip = _peer_ip, + server_name = _server_name, + login_auth_method=_auth_method, + login_type=_login_type + ) + db.session.add(auth_log_add) + db.session.commit() + + def authlog_flush(): + AuthLog.query.delete() + db.session.commit() @app.route('/add_user', methods=['POST', 'GET']) @@ -1107,26 +1255,28 @@ def create_app(): @app.route('/auth', methods=['POST']) def auth(): hblink_req = request.json -## print((hblink_req)) + print((hblink_req)) if hblink_req['secret'] in shared_secrets: - if 'login_id' in hblink_req: + if 'login_id' in hblink_req and 'login_confirmed' not in hblink_req: if type(hblink_req['login_id']) == int: if authorized_peer(hblink_req['login_id'])[0]: if isinstance(authorized_peer(hblink_req['login_id'])[1], int) == True: - mmdvm_logins.append([hblink_req['login_id'], authorized_peer(hblink_req['login_id'])[2], authorized_peer(hblink_req['login_id'])[1], 'Calculated', time.time()]) + #mmdvm_logins.append([hblink_req['login_id'], authorized_peer(hblink_req['login_id'])[2], authorized_peer(hblink_req['login_id'])[1], hblink_req['login_ip'], time.time()]) + authlog_add(hblink_req['login_id'], hblink_req['login_ip'], hblink_req['login_server'], authorized_peer(hblink_req['login_id'])[2], gen_passphrase(hblink_req['login_id']), 'Attempt') response = jsonify( allow=True, mode='normal', ) elif authorized_peer(hblink_req['login_id'])[1] == '': - # normal - mmdvm_logins.append([hblink_req['login_id'], authorized_peer(hblink_req['login_id'])[2], authorized_peer(hblink_req['login_id'])[1], 'Legacy', time.time()]) + #mmdvm_logins.append([hblink_req['login_id'], authorized_peer(hblink_req['login_id'])[2], authorized_peer(hblink_req['login_id'])[1], hblink_req['login_ip'], time.time()]) + authlog_add(hblink_req['login_id'], hblink_req['login_ip'], hblink_req['login_server'], authorized_peer(hblink_req['login_id'])[2], 'Config: ' + legacy_passphrase, 'Attempt') response = jsonify( allow=True, mode='legacy', ) elif authorized_peer(hblink_req['login_id'])[1] != '' or isinstance(authorized_peer(hblink_req['login_id'])[1], int) == False: - mmdvm_logins.append([hblink_req['login_id'], authorized_peer(hblink_req['login_id'])[2], authorized_peer(hblink_req['login_id'])[1], 'Custom', time.time()]) + #mmdvm_logins.append([hblink_req['login_id'], authorized_peer(hblink_req['login_id'])[2], authorized_peer(hblink_req['login_id'])[1], hblink_req['login_ip'], time.time()]) + authlog_add(hblink_req['login_id'], hblink_req['login_ip'], hblink_req['login_server'], authorized_peer(hblink_req['login_id'])[2], authorized_peer(hblink_req['login_id'])[1], 'Attempt') print(authorized_peer(hblink_req['login_id'])) response = jsonify( allow=True, @@ -1158,6 +1308,14 @@ def create_app(): msg = jsonify(auth=False, reason='Incorrect password') response = make_response(msg, 401) + elif 'login_id' in hblink_req and 'login_confirmed' in hblink_req: + if hblink_req['old_auth'] == True: + authlog_add(hblink_req['login_id'], hblink_req['login_ip'], hblink_req['login_server'], authorized_peer(hblink_req['login_id'])[2], 'CONFIG PASSPHRASE', 'Confirmed') + else: + authlog_add(hblink_req['login_id'], hblink_req['login_ip'], hblink_req['login_server'], authorized_peer(hblink_req['login_id'])[2], 'USER MANAGER', 'Confirmed') + response = jsonify( + logged=True + ) elif hblink_req['burn_list']: # == 'burn_list': response = jsonify( burn_list=get_burnlist() diff --git a/user_managment/templates/flask_user_layout.html b/user_managment/templates/flask_user_layout.html index a46f4ef..bbdf8f7 100644 --- a/user_managment/templates/flask_user_layout.html +++ b/user_managment/templates/flask_user_layout.html @@ -48,7 +48,7 @@ Add a User Edit Users Waiting Approval - Auth Log + Auth Log {% endif %} Help