From af7e24a94868b590215882d89d7c8e1d9caf1e50 Mon Sep 17 00:00:00 2001 From: Simon Cloutier Date: Thu, 3 Apr 2025 15:58:07 -0400 Subject: [PATCH] working for the most part --- app/__pycache__/app.cpython-39.pyc | Bin 1520 -> 1455 bytes app/app.py | 6 +- app/db_connection.py | 11 + app/db_interface.py | 78 +++++-- app/static/styles.css | 62 ++++- app/templates/base.html | 2 +- app/templates/group_list.html | 2 +- app/templates/index.html | 35 ++- app/templates/stats.html | 213 ++++++++++-------- app/templates/user_list.html | 4 +- .../__pycache__/group_views.cpython-39.pyc | Bin 1348 -> 1358 bytes .../__pycache__/index_views.cpython-39.pyc | Bin 3136 -> 2084 bytes .../__pycache__/user_views.cpython-39.pyc | Bin 1827 -> 1869 bytes app/views/group_views.py | 4 +- app/views/index_views.py | 30 +-- app/views/stats_views.py | 82 +++++++ app/views/user_views.py | 4 +- db/init/init-schema.sql | 2 +- 18 files changed, 367 insertions(+), 168 deletions(-) create mode 100644 app/db_connection.py create mode 100644 app/views/stats_views.py diff --git a/app/__pycache__/app.cpython-39.pyc b/app/__pycache__/app.cpython-39.pyc index 48dca6f5ce18aea52e1716865a145fccaef281b7..03f42eee7c0ba9f9c81c93a4702c9cbfe7beac93 100644 GIT binary patch delta 577 zcmY*VK}+LM5Y9{6w5Cm(q_I_N(^_4j2nCv+D%efgC$~?VMQ|QlA(F93blfyHX!>|X?fy; z?P-~4*cnFRf!$`c?HLZ$ea)ty0#UGX=y`>Dlv=6$Q2CHu|b2=J`U5dx0g;pUs>GU3(-agaV}`u6t# ze?%YUwrz>|0+pv)_xR{A=y6^ANw2fK_@BP2my%g-;t~Zud)F&+#9gK?erG&`nQD&d HshK?k@!N&H delta 624 zcmZ{gJ#X4j6o!3maKK<=3=SVLBn5_`NH9l?S*uo6he}=$<(yKG^Py{3YO`cW#necp zSIU&ZjG40aA9U>x$k3&0hyH;|J-&q&sja1Z{rb7@dGGPL%YDz8Bhxe#dVYNRHhH%& z8|=~I;K_w=f&zMA`4+vmg2J)w+e|@*#Y^Tp=*)6)3G+)vEYW4iEE)g2(pfN;s_&vT z+on~CtLQEnq=6SEeRrz=om> zb7oC6z*&FAyHKL5%HY0LsL?%ih-?$7Kvj|llF$`UhaE{;l6XWK(3GSNdg`gt-4%28 zSbSlzn5)lpn?}TK@l|crJd&ZB%uMAD?dMzl2|s;x7K(-16Av}#s6dqes`#ynr0$CW zPDbN5L+Kv|(=gg2ybddoKG*wUG>SrTqa7Sd)q5Eo2e*u;>GYqWX=&?h1i9i`8w_~) ze-+mB1n~8cBTPAtqy3H23h8b~SgGg6y*@rYc`=>vtoV>>Gedk#z3(JH%#%jBn{0{w Tucid1X?SU`pk|n%{aHT&IsA)9 diff --git a/app/app.py b/app/app.py index 12ee52e..7510aac 100644 --- a/app/app.py +++ b/app/app.py @@ -2,6 +2,7 @@ from flask import Flask, redirect, url_for, render_template from views.index_views import index from views.user_views import user from views.group_views import group +from views.stats_views import stats from config import app_config @@ -24,6 +25,7 @@ app.logger.setLevel(logging.INFO) app.register_blueprint(index) app.register_blueprint(user, url_prefix='/user') app.register_blueprint(group, url_prefix='/group') +app.register_blueprint(stats, url_prefix='/stats') @app.route('/user_list') def legacy_user_list(): @@ -33,10 +35,6 @@ def legacy_user_list(): def legacy_group_list(): return redirect(url_for('group.group_list')) -@app.route('/stats') -def stats(): - return render_template('stats.html') - @app.route('/') def index_redirect(): return render_template('index.html') diff --git a/app/db_connection.py b/app/db_connection.py new file mode 100644 index 0000000..5742f2c --- /dev/null +++ b/app/db_connection.py @@ -0,0 +1,11 @@ +import mysql.connector +import os + +def get_connection(): + return mysql.connector.connect( + host=os.getenv('DB_HOST'), + port=int(os.getenv('DB_PORT', 3306)), + user=os.getenv('DB_USER'), + password=os.getenv('DB_PASSWORD'), + database=os.getenv('DB_NAME'), + ) diff --git a/app/db_interface.py b/app/db_interface.py index b2a5eee..7af9983 100644 --- a/app/db_interface.py +++ b/app/db_interface.py @@ -4,14 +4,8 @@ import datetime import requests import time import os - -def get_connection(): - return mysql.connector.connect( - host=current_app.config['DB_HOST'], - user=current_app.config['DB_USER'], - password=current_app.config['DB_PASSWORD'], - database=current_app.config['DB_NAME'] - ) +import pytz +from db_connection import get_connection def get_all_users(): @@ -45,10 +39,10 @@ def get_all_groups(): FROM groups g ORDER BY g.vlan_id """) - groups = cursor.fetchall() + available_groups = cursor.fetchall() cursor.close() conn.close() - return groups + return available_groups @@ -106,6 +100,7 @@ def duplicate_group(vlan_id): def add_user(mac_address, description, vlan_id): + print(f"→ Adding to DB: mac={mac_address}, desc={description}, vlan={vlan_id}") conn = get_connection() cursor = conn.cursor() cursor.execute( @@ -144,19 +139,61 @@ def delete_user(mac_address): conn.close() -def get_latest_auth_logs(result, limit=10): +def get_latest_auth_logs(reply_type=None, limit=5, time_range=None): conn = get_connection() cursor = conn.cursor(dictionary=True) - cursor.execute( - "SELECT * FROM auth_logs WHERE result = %s ORDER BY timestamp DESC LIMIT %s", - (result, limit) - ) + + # Determine the time filter based on the time_range + if time_range: + now = datetime.now(pytz.timezone(current_app.config.get('APP_TIMEZONE', 'UTC'))) + if time_range == 'last_minute': + time_filter = now - timedelta(minutes=1) + elif time_range == 'last_5_minutes': + time_filter = now - timedelta(minutes=5) + elif time_range == 'last_10_minutes': + time_filter = now - timedelta(minutes=10) + elif time_range == 'last_hour': + time_filter = now - timedelta(hours=1) + elif time_range == 'last_6_hours': + time_filter = now - timedelta(hours=6) + elif time_range == 'last_12_hours': + time_filter = now - timedelta(hours=12) + elif time_range == 'last_day': + time_filter = now - timedelta(days=1) + elif time_range == 'last_30_days': + time_filter = now - timedelta(days=30) + else: # 'all' case + time_filter = None + + if time_filter: + cursor.execute(""" + SELECT * FROM auth_logs + WHERE reply = %s AND timestamp >= %s + ORDER BY timestamp DESC + LIMIT %s + """, (reply_type, time_filter, limit)) + else: + cursor.execute(""" + SELECT * FROM auth_logs + WHERE reply = %s + ORDER BY timestamp DESC + LIMIT %s + """, (reply_type, limit)) + else: + cursor.execute(""" + SELECT * FROM auth_logs + WHERE reply = %s + ORDER BY timestamp DESC + LIMIT %s + """, (reply_type, limit)) + logs = cursor.fetchall() cursor.close() conn.close() return logs + def get_vendor_info(mac, insert_if_found=True): conn = get_connection() cursor = conn.cursor(dictionary=True) @@ -410,3 +447,14 @@ def lookup_mac_verbose(mac): conn.close() return "\n".join(output) +def get_user_by_mac(mac_address): + conn = get_connection() + cursor = conn.cursor(dictionary=True) + + cursor.execute(""" + SELECT * FROM users WHERE mac_address = %s + """, (mac_address,)) + user = cursor.fetchone() + cursor.close() + conn.close() + return user diff --git a/app/static/styles.css b/app/static/styles.css index 2184883..39820b3 100644 --- a/app/static/styles.css +++ b/app/static/styles.css @@ -115,7 +115,12 @@ table.styled-table { .styled-table th { background-color: var(--header-bg); color: var(--fg); - text-align: left; + text-align: center; +} + +/* 🧩 Fix: Remove right border from last column */ +.styled-table thead th:last-child { + border-right: none; } .styled-table input[type="text"], @@ -235,4 +240,57 @@ form.inline-form { font-size: 0.9rem; white-space: pre-wrap; margin-top: 1em; -} \ No newline at end of file +} + +.stats-page .stats-container { + display: flex; + flex-wrap: wrap; + gap: 2rem; +} + +.stats-page .card { + flex: 1; + min-width: 45%; + padding: 1rem; + border-radius: 8px; + background-color: var(--card-bg); + color: var(--fg); + box-shadow: 0 0 10px rgba(0,0,0,0.2); +} + +.stats-page .success-card { + border-left: 6px solid limegreen; +} + +.stats-page .error-card { + border-left: 6px solid crimson; +} + +.stats-page .fallback-card { + border-left: 6px solid orange; +} + +.stats-page .styled-table.small-table td, +.stats-page .styled-table.small-table th { + padding: 6px; + font-size: 0.9rem; +} + +.stats-page form.inline-form { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: nowrap; + white-space: nowrap; +} + +.stats-page form.inline-form select { + flex: 1; + min-width: 140px; + max-width: 100%; +} + +.stats-page form.inline-form button { + flex: 0 0 auto; + padding: 6px; +} diff --git a/app/templates/base.html b/app/templates/base.html index e058767..c28eebb 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -12,7 +12,7 @@ Home Users Groups - Stats + Stats
diff --git a/app/templates/group_list.html b/app/templates/group_list.html index 0af8c34..3835df2 100644 --- a/app/templates/group_list.html +++ b/app/templates/group_list.html @@ -20,7 +20,7 @@ - {% for group in groups %} + {% for group in available_groups %} {{ group.vlan_id }} diff --git a/app/templates/index.html b/app/templates/index.html index df15bbe..cc02df1 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -15,30 +15,27 @@
-

Recent Access-Accept

-
    - {% for entry in latest_accept %} +

    Recent Access Logs

    +
      +
    • Access-Accept Logs
    • + {% for log in latest_accept %}
    • - {{ entry.mac_address }} - {% if entry.description %} ({{ entry.description }}){% endif %} - — {{ entry.ago }} + {{ log.mac_address }} - {{ log.reply }} +
      + {{ log.timestamp }} - {{ log.result }} +
    • + {% endfor %} + +
    • Access-Reject Logs
    • + {% for log in latest_reject %} +
    • + {{ log.mac_address }} - {{ log.reply }} +
      + {{ log.timestamp }} - {{ log.result }}
    • {% endfor %}
    -

    Recent Access-Reject

    -
      - {% for entry in latest_reject %} -
    • - {{ entry.mac_address }} - {% if entry.description %} ({{ entry.description }}){% endif %} - — {{ entry.ago }} -
    • - {% endfor %} -
    - -
    -

    MAC Vendor Lookup

    diff --git a/app/templates/stats.html b/app/templates/stats.html index c83deeb..50a9849 100644 --- a/app/templates/stats.html +++ b/app/templates/stats.html @@ -2,99 +2,132 @@ {% block title %}Authentication Stats{% endblock %} {% block content %} +
    +

    Authentication Stats

    -
    -
    -

    Recent Access-Accept

    - - - - - - - - - - - {% for entry in accept_entries %} - - - - - - - {% endfor %} - -
    MAC AddressDescriptionVendorTime
    {{ entry.mac_address }}{{ entry.description or '' }}{{ entry.vendor }}{{ entry.ago }}
    -
    + + + + + -
    -

    Recent Access-Reject

    - - - - - - - - - - - - {% for entry in reject_entries %} - - - - - - - - {% endfor %} - -
    MAC AddressDescriptionVendorTimeActions
    {{ entry.mac_address }}{{ entry.description or '' }}{{ entry.vendor }}{{ entry.ago }} - {% if entry.already_exists %} - Already exists in VLAN {{ entry.existing_vlan or 'unknown' }} - {% else %} -
    - - - -
    - {% endif %} -
    -
    +
    + +
    +

    Recent Access-Accept

    + + + + + + + + + + + {% for entry in accept_entries %} + + + + + + + {% endfor %} + +
    MAC AddressDescriptionVendorTime
    {{ entry.mac_address }}{{ entry.description or '' }}{{ entry.vendor }}{{ entry.ago }}
    +
    + + +
    +

    Recent Access-Reject

    + + + + + + + + + + + {% for entry in reject_entries %} + + + + + + + {% endfor %} + +
    MAC AddressDescriptionVendorTime
    {{ entry.mac_address }}{{ entry.description or '' }}{{ entry.vendor }}{{ entry.ago }}
    +
    + + +
    +

    Recent Access-Fallback

    + + + + + + + + + + + + {% if fallback_entries %} + {% for entry in fallback_entries %} + + + + + + + + + + + {% endfor %} + {% else %} + + {% endif %} + +
    MAC AddressDescriptionVendorTimeActions
    {{ entry.mac_address }} + {% if not entry.already_exists %} + + {% else %} + {{ entry.description or '' }} + {% endif %} + {{ entry.vendor }}{{ entry.ago }} + {% if not entry.already_exists %} +
    + + + +
    + {% else %} + Already exists in VLAN {{ entry.existing_vlan or 'unknown' }} + {% endif %} +
    No data available.
    +
    - +
    {# closes .stats-page #} {% endblock %} diff --git a/app/templates/user_list.html b/app/templates/user_list.html index 94b70c7..95fe078 100644 --- a/app/templates/user_list.html +++ b/app/templates/user_list.html @@ -9,7 +9,7 @@ @@ -44,7 +44,7 @@