Update syntax and improve importations

This commit is contained in:
2024-06-04 07:35:03 +00:00
parent 83fcd383b7
commit e9246bb376
8 changed files with 574 additions and 317 deletions
+62 -39
View File
@@ -1,35 +1,50 @@
from threading import Thread
from flask import Flask, render_template, send_file, Response, request, redirect, jsonify
from flask import (
Flask,
render_template,
send_file,
Response,
request,
redirect,
jsonify,
)
import io
from ldapSync import sync_ldap_to_database
from database import *
from env import *
from database import (WebServerPORT, add_door_to_database, check_access, delete_group_from_database,
get_doors, get_existing_groups, get_latest_logs, get_logs, get_users,
log_access_attempt)
from env import DBFILE
app = Flask(__name__)
# Route to the home
@app.route('/')
@app.route("/")
def index():
existing_groups = get_existing_groups(DBFILE) # Update with your database file path
logs = get_latest_logs(DBFILE,5)
#print(logs[0])
return render_template('./index.html', existing_groups=existing_groups, logs=logs)
logs = get_latest_logs(DBFILE, 5)
# print(logs[0])
return render_template("./index.html", existing_groups=existing_groups, logs=logs)
# Route to display the fuser db
@app.route('/UserDB')
@app.route("/UserDB")
def usersdb():
users = get_users()
return render_template('userdb.html', users=users)
return render_template("userdb.html", users=users)
# Route to display the fuser db
@app.route('/LogsDB')
@app.route("/LogsDB")
def logsdb():
logs = get_logs()
return render_template('logsdb.html', logs=logs)
return render_template("logsdb.html", logs=logs)
@app.route('/export_logs')
@app.route("/export_logs")
def export_logs():
logs = get_logs()
# Create a file-like string to write logs
log_output = io.StringIO()
log_line = "TimeStamp,User,Tag UID,Door ID,Granted,\n"
@@ -37,75 +52,83 @@ def export_logs():
for log in logs:
log_line = f"{log[0]},{log[1]},{log[2]},{log[4]},{'Yes' if log[3] else 'No'},\n"
log_output.write(log_line)
# Set the position to the beginning of the stream
log_output.seek(0)
# Create a response with the file data
return Response(
log_output,
mimetype="text/plain",
headers={"Content-disposition": "attachment; filename=logs.csv"}
headers={"Content-disposition": "attachment; filename=logs.csv"},
)
@app.route('/GroupsDB')
@app.route("/GroupsDB")
def groupsdb():
doors = get_doors()
groups = get_existing_groups(DBFILE)
return render_template('groupsdb.html', doors=doors, groups=groups)
return render_template("groupsdb.html", doors=doors, groups=groups)
@app.route('/delete_group/<group_cn>', methods=['POST'])
@app.route("/delete_group/<group_cn>", methods=["POST"])
def delete_group(group_cn):
delete_group_from_database(group_cn)
return render_template('./index.html')
return render_template("./index.html")
# Route to handle form submission and add the door to the database
@app.route('/add_door', methods=['POST'])
@app.route("/add_door", methods=["POST"])
def add_door():
Door_id = request.form["Door_id"]
group_cn = request.form["group_cn"]
# Update with your database file path
# Update with your database file path
exec = add_door_to_database(DBFILE, group_cn, Door_id)
if add_door_to_database(DBFILE, group_cn, Door_id):
return redirect('/')
return redirect("/")
else:
return f"Failed to add door to the database."
# Route to handle sync button click
@app.route('/sync')
@app.route("/sync")
def sync():
sync_ldap_to_database(DBFILE)
return render_template('./LDAP.html')
return render_template("./LDAP.html")
# Route to handle door access requests
@app.route('/access', methods=['POST'])
@app.route("/access", methods=["POST"])
def door_access():
data = request.get_json()
rfid_uid = data.get('rfid_uid')
door_id = data.get('door_id')
rfid_uid = data.get("rfid_uid")
door_id = data.get("door_id")
if rfid_uid is None or door_id is None:
return jsonify({'error': 'RFID UID and door ID are required'}), 400
return jsonify({"error": "RFID UID and door ID are required"}), 400
access_granted, upn = check_access(rfid_uid, door_id)
if access_granted:
print('')
log_access_attempt(DBFILE,upn,rfid_uid,True,door_id)
return jsonify({'access_granted': True, 'upn': upn}), 200
print("")
log_access_attempt(DBFILE, upn, rfid_uid, True, door_id)
return jsonify({"access_granted": True, "upn": upn}), 200
else:
log_access_attempt(DBFILE,upn,rfid_uid,False,door_id)
return jsonify({'access_granted': False}), 403
log_access_attempt(DBFILE, upn, rfid_uid, False, door_id)
return jsonify({"access_granted": False}), 403
def run_flask_app():
app.run(debug=True, use_reloader=False, port=5000, host="0.0.0.0")
def run_webServer_thread():
print(f'STARTING WEB SERVER ON PORT {WebServerPORT}')
print(f"STARTING WEB SERVER ON PORT {WebServerPORT}")
flask_thread = Thread(target=run_flask_app, daemon=True)
flask_thread.start()
# flask_thread.join()
if __name__ == '__main__':
if __name__ == "__main__":
app.run(debug=True)
View File
+84 -45
View File
@@ -1,42 +1,50 @@
from datetime import datetime
import sqlite3
from env import *
from env import DBFILE
# Function to check if a table exists in the database
def table_exists(cursor, table_name):
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
cursor.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,)
)
return cursor.fetchone() is not None
# Function to create the Users table
def create_users_table(cursor):
cursor.execute('''CREATE TABLE Users (
cursor.execute("""CREATE TABLE Users (
upn TEXT PRIMARY KEY,
rFIDUID TEXT,
MemberOf TEXT,
FOREIGN KEY (MemberOf) REFERENCES Groups(cn)
)''')
)""")
# Function to create the Groups table
def create_groups_table(cursor):
cursor.execute('''CREATE TABLE Groups (
cursor.execute("""CREATE TABLE Groups (
cn TEXT PRIMARY KEY
)''')
)""")
# Function to create the Doors table
def create_doors_table(cursor):
cursor.execute('''CREATE TABLE Doors (
cursor.execute("""CREATE TABLE Doors (
id INTEGER PRIMARY KEY,
GroupCn TEXT,
FOREIGN KEY (GroupCn) REFERENCES Groups(cn)
)''')
)""")
# Function to create the logs table
def create_logs_table(cursor):
"""
Create a log table with columns id, timestamp, user, and granted.
:param db_file: The database file path.
"""
cursor.execute('''
cursor.execute("""
CREATE TABLE IF NOT EXISTS log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT ,
@@ -48,7 +56,9 @@ def create_logs_table(cursor):
FOREIGN KEY (user) REFERENCES Users (upn)
FOREIGN KEY (rFIDUID) REFERENCES Users (rFIDUID)
)
''')
""")
# Function to setup the database
def setup_database(db_file):
# Connect to the SQLite database
@@ -85,10 +95,11 @@ def setup_database(db_file):
conn.commit()
conn.close()
def log_access_attempt(db_file, user, rFIDUID, granted, doorID):
"""
Log an access attempt to the log table.
:param db_file: The database file path.
:param user: The user attempting access.
:param rFIDUID: The user's tag uid
@@ -97,15 +108,19 @@ def log_access_attempt(db_file, user, rFIDUID, granted, doorID):
"""
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
print(f'[{datetime.now()}] User {user} get granted : {granted} on door : {doorID}')
cursor.execute('''
print(f"[{datetime.now()}] User {user} get granted : {granted} on door : {doorID}")
cursor.execute(
"""
INSERT INTO log (timestamp, user, rFIDUID, granted, door_id) VALUES (?, ?, ?, ?, ?)
''', (datetime.now(), user, rFIDUID, granted, doorID))
""",
(datetime.now(), user, rFIDUID, granted, doorID),
)
conn.commit()
conn.close()
def print_users_table(cursor):
cursor.execute("SELECT * FROM Users")
rows = cursor.fetchall()
@@ -113,6 +128,7 @@ def print_users_table(cursor):
for row in rows:
print(row)
# Function to print the content of the Groups table
def print_groups_table(cursor):
cursor.execute("SELECT * FROM Groups")
@@ -121,6 +137,7 @@ def print_groups_table(cursor):
for row in rows:
print(row)
# Function to print the content of the Doors table
def print_doors_table(cursor):
cursor.execute("SELECT * FROM Doors")
@@ -128,6 +145,8 @@ def print_doors_table(cursor):
print("Doors:")
for row in rows:
print(row)
# Function to print the content of the Log table
def print_log_table(cursor):
cursor.execute("SELECT * FROM log")
@@ -136,59 +155,65 @@ def print_log_table(cursor):
for row in rows:
print(row)
# Function to print the content of the entire database
def print_database_content(db_file):
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
print_users_table(cursor)
print_groups_table(cursor)
print_doors_table(cursor)
#print_log_table(cursor)
conn.close()
def get_logs():
# print_log_table(cursor)
conn.close()
def get_logs():
"""
Fetch all logs from the log table in the database.
:return: List of log records.
"""
conn = sqlite3.connect(DBFILE)
cursor = conn.cursor()
cursor.execute('''
cursor.execute("""
SELECT timestamp, user, rFIDUID, granted, door_id
FROM log
ORDER BY id DESC
''')
""")
logs = cursor.fetchall()
conn.close()
return logs
def get_latest_logs(db_file,limit=10):
def get_latest_logs(db_file, limit=10):
"""
Fetch the latest logs from the database.
:param limit: The number of latest logs to fetch.
:return: List of log entries.
"""
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
cursor.execute('''
cursor.execute(
"""
SELECT timestamp, user, rFIDUID, granted, door_id
FROM log
ORDER BY id DESC
LIMIT ?
''', (limit,))
""",
(limit,),
)
logs = cursor.fetchall()
conn.close()
return logs
# Function to fetch list of existing groups from the database
def get_existing_groups(db_file):
try:
@@ -201,6 +226,8 @@ def get_existing_groups(db_file):
except sqlite3.Error as e:
print(f"SQLite Error: {e}")
return []
def delete_group_from_database(group_cn):
conn = sqlite3.connect(DBFILE)
cursor = conn.cursor()
@@ -208,6 +235,7 @@ def delete_group_from_database(group_cn):
conn.commit()
conn.close()
def get_doors():
conn = sqlite3.connect(DBFILE)
cursor = conn.cursor()
@@ -216,6 +244,7 @@ def get_doors():
conn.close()
return doors
def get_users():
"""
Fetch all users from the Users table in the database.
@@ -223,28 +252,36 @@ def get_users():
"""
conn = sqlite3.connect(DBFILE)
cursor = conn.cursor()
cursor.execute('SELECT upn, rFIDUID, MemberOf FROM Users')
cursor.execute("SELECT upn, rFIDUID, MemberOf FROM Users")
users = cursor.fetchall()
conn.close()
return users
# Function to add a door to the database
def add_door_to_database(db_file, group_cn, Door_id):
try:
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
cursor.execute("INSERT INTO Doors (id, GroupCn) VALUES (?,?)", (Door_id,group_cn,))
cursor.execute(
"INSERT INTO Doors (id, GroupCn) VALUES (?,?)",
(
Door_id,
group_cn,
),
)
conn.commit()
conn.close()
#print_database_content(DBFILE)
# print_database_content(DBFILE)
return True
except sqlite3.Error as e:
#print_database_content(DBFILE)
# print_database_content(DBFILE)
print(f"SQLite Error: {e}")
return (False, e)
# Function to verify if the user is allowed to open the door
def check_access(rfid_uid_str, door_id):
try:
@@ -252,10 +289,12 @@ def check_access(rfid_uid_str, door_id):
cursor = conn.cursor()
# Convert the received RFID UID string to bytes
rfid_uid_bytes = rfid_uid_str.encode('utf-8')
rfid_uid_bytes = rfid_uid_str.encode("utf-8")
# Get the user's UPN and group memberships based on the RFID UID
cursor.execute("SELECT upn, MemberOf FROM Users WHERE rFIDUID = ?", (rfid_uid_bytes,))
cursor.execute(
"SELECT upn, MemberOf FROM Users WHERE rFIDUID = ?", (rfid_uid_bytes,)
)
user_data = cursor.fetchone()
if user_data is None:
return False, None # User not found
@@ -263,7 +302,7 @@ def check_access(rfid_uid_str, door_id):
upn_bytes, user_groups = user_data
# Decode the UPN bytes to string
upn = upn_bytes.decode('utf-8')
upn = upn_bytes.decode("utf-8")
# Get the group associated with the door
cursor.execute("SELECT GroupCn FROM Doors WHERE id = ?", (door_id,))
@@ -274,11 +313,11 @@ def check_access(rfid_uid_str, door_id):
door_group = door_group[0]
# Check if the user's group is allowed to open the door
if door_group in user_groups.split(','):
if door_group in user_groups.split(","):
return True, upn # Access granted
else:
return False, None # Access denied
except sqlite3.Error as e:
print(f"SQLite Error: {e}")
return False, None
return False, None
+60 -31
View File
@@ -3,7 +3,8 @@ import ldap
import sqlite3
import threading
import schedule
from env import *
from env import DOOR_ACCESS_GROUPS_DN, LDAPPASS, LDAPUSER, LDAP_SERVER, USERS_DN
# Function to initialize LDAP connection
def initialize_ldap_connection():
@@ -11,8 +12,8 @@ def initialize_ldap_connection():
## Settings :
None
## Behavior
Init the connection to the LDAP server.
Return LDAPobjet instance when connected
Init the connection to the LDAP server.
Return LDAPobjet instance when connected
if it fail, return None and print error code
"""
try:
@@ -22,43 +23,50 @@ def initialize_ldap_connection():
print(f"[{datetime.now()}] LDAP connection successful.")
return connect
except ldap.LDAPError as e:
print(f'[{datetime.now()}] LDAP Error: {e}')
print(f"[{datetime.now()}] LDAP Error: {e}")
return None
# Function to retrieve users from LDAP
def retrieve_users_from_ldap(ldap_connection):
"""
## Settings :
- ldap_connection : LDAPobjet instance
## Behavior
retrieve the users in the specified OU
Return result when it success
if it fail, return empty list and print error code
## Settings :
- ldap_connection : LDAPobjet instance
## Behavior
retrieve the users in the specified OU
Return result when it success
if it fail, return empty list and print error code
"""
try:
result = ldap_connection.search_s(USERS_DN, ldap.SCOPE_SUBTREE, '(objectClass=user)')
result = ldap_connection.search_s(
USERS_DN, ldap.SCOPE_SUBTREE, "(objectClass=user)"
)
return result
except ldap.LDAPError as e:
print(f'[{datetime.now()}] LDAP Error: {e}')
print(f"[{datetime.now()}] LDAP Error: {e}")
return []
# Function to retrieve groups from LDAP
def retrieve_groups_from_ldap(ldap_connection):
"""
## Settings :
- ldap_connection : LDAPobjet instance
## Behavior
retrieve the groups in the specified OU
Return result when it success
retrieve the groups in the specified OU
Return result when it success
if it fail, return empty list and print error code
"""
try:
result = ldap_connection.search_s(DOOR_ACCESS_GROUPS_DN, ldap.SCOPE_SUBTREE, '(objectClass=group)')
result = ldap_connection.search_s(
DOOR_ACCESS_GROUPS_DN, ldap.SCOPE_SUBTREE, "(objectClass=group)"
)
return result
except ldap.LDAPError as e:
print(f'[{datetime.now()}]LDAP Error: {e}')
print(f"[{datetime.now()}]LDAP Error: {e}")
return []
# Function to add user to the database or update if already exists
def add_user_to_database(conn, cursor, upn, rfid_uid, member_of):
try:
@@ -67,18 +75,27 @@ def add_user_to_database(conn, cursor, upn, rfid_uid, member_of):
if existing_user:
# User already exists, check if data needs to be updated
if existing_user[1] != rfid_uid or existing_user[2] != member_of:
cursor.execute("UPDATE Users SET rFIDUID=?, MemberOf=? WHERE upn=?", (rfid_uid, member_of, upn))
cursor.execute(
"UPDATE Users SET rFIDUID=?, MemberOf=? WHERE upn=?",
(rfid_uid, member_of, upn),
)
conn.commit()
print(f"[{datetime.now()}] User '{upn}' updated in the database.")
else:
print(f"[{datetime.now()}] User '{upn}' already exists in the database with the same data.")
print(
f"[{datetime.now()}] User '{upn}' already exists in the database with the same data."
)
else:
# User doesn't exist, insert new user
cursor.execute("INSERT INTO Users (upn, rFIDUID, MemberOf) VALUES (?, ?, ?)", (upn, rfid_uid, member_of))
cursor.execute(
"INSERT INTO Users (upn, rFIDUID, MemberOf) VALUES (?, ?, ?)",
(upn, rfid_uid, member_of),
)
conn.commit()
print(f"[{datetime.now()}] User '{upn}' added to the database.")
except sqlite3.Error as e:
print(f'SQLite Error: {e}')
print(f"SQLite Error: {e}")
# Function to add group to the database or update if already exists
def add_group_to_database(conn, cursor, cn):
@@ -94,7 +111,8 @@ def add_group_to_database(conn, cursor, cn):
conn.commit()
print(f"[{datetime.now()}] Group '{cn}' added to the database.")
except sqlite3.Error as e:
print(f'SQLite Error: {e}')
print(f"SQLite Error: {e}")
# Function to sync LDAP users and groups to the database
def sync_ldap_to_database(db_file):
@@ -124,41 +142,52 @@ def sync_ldap_to_database(db_file):
# Retrieve users from LDAP and add them to the database
users = retrieve_users_from_ldap(ldap_conn)
for dn, user_info in users:
upn = user_info.get('userPrincipalName', [''])[0]
rfid_uid = user_info.get('rFIDUID', [''])[0]
member_of = [group.decode('utf-8').split(',')[0].split('=')[1] for group in user_info.get('memberOf', [])]
upn = user_info.get("userPrincipalName", [""])[0]
rfid_uid = user_info.get("rFIDUID", [""])[0]
member_of = [
group.decode("utf-8").split(",")[0].split("=")[1]
for group in user_info.get("memberOf", [])
]
# Check if the user is disabled in LDAP
user_account_control = user_info.get('userAccountControl', [0])[0]
if user_account_control == b'514' or user_account_control == b'66050': # Check if the 9th bit is set (ADS_UF_ACCOUNTDISABLE flag)
user_account_control = user_info.get("userAccountControl", [0])[0]
if (
user_account_control == b"514" or user_account_control == b"66050"
): # Check if the 9th bit is set (ADS_UF_ACCOUNTDISABLE flag)
# User is disabled, check if user exists in the database and remove if present
cursor.execute("SELECT * FROM Users WHERE upn=?", (upn,))
existing_user = cursor.fetchone()
if existing_user:
cursor.execute("DELETE FROM Users WHERE upn=?", (upn,))
conn.commit()
print(f"[{datetime.now()}] User '{upn}' disabled in LDAP and removed from the database.")
print(
f"[{datetime.now()}] User '{upn}' disabled in LDAP and removed from the database."
)
else:
print(f"[{datetime.now()}] User '{upn}' disabled in LDAP but not present in the database.")
print(
f"[{datetime.now()}] User '{upn}' disabled in LDAP but not present in the database."
)
continue # Skip adding the disabled user to the database
# User is not disabled, add or update user in the database
add_user_to_database(conn, cursor, upn, rfid_uid, ', '.join(member_of))
add_user_to_database(conn, cursor, upn, rfid_uid, ", ".join(member_of))
# Retrieve groups from LDAP and add them to the database
groups = retrieve_groups_from_ldap(ldap_conn)
for dn, group_info in groups:
cn = group_info.get('cn', [''])[0].decode('utf-8')
cn = group_info.get("cn", [""])[0].decode("utf-8")
add_group_to_database(conn, cursor, cn)
# Close connections
conn.close()
ldap_conn.unbind()
def run_sync_ldap_to_database_thread(db_file):
print(f"[{datetime.now()}] Running LDAP sync")
threading.Thread(target=sync_ldap_to_database, args=(db_file,), daemon=True).start()
def schedule_sync_ldap_to_database(db_file):
run_sync_ldap_to_database_thread(db_file) # Run immediately
schedule.every(5).minutes.do(run_sync_ldap_to_database_thread, db_file)
schedule.every(5).minutes.do(run_sync_ldap_to_database_thread, db_file)
+6 -7
View File
@@ -1,15 +1,14 @@
from ldapSync import *
from database import *
from ldapSync import schedule_sync_ldap_to_database
from database import setup_database, print_database_content
from Webserver import run_webServer_thread
from env import *
from env import DBFILE
import schedule
setup_database(DBFILE)
print_database_content(DBFILE)
#print_database_content(DBFILE)
run_webServer_thread()
schedule_sync_ldap_to_database(DBFILE)
while True :
schedule.run_pending()
while True:
schedule.run_pending()