Skip to content

Add Configuration Module (Web Settings UI with Auth and Lockfile) #50

@KD3BVX

Description

@KD3BVX

Overview

Implement a self-contained config_module/ directory that adds a gear icon (⚙) to the dashboard for editing station and display settings via the web. The module is designed to be fully removable: deleting config_module/ disables the feature completely with no residual errors.

Design Principles

  • Secrets only in .env: callsign, grid, and display settings move out of .env and into settings.json managed by this module
  • Modular: all feature code in config_module/; existing files minimally touched
  • Removable: rm -rf config_module/ is sufficient to disable the feature entirely ("ultimate security")

Settings Managed

  • Admin username, receiver callsign, receiver grid square
  • Default last interval, highlight threshold, contest bands
  • Map initial view (lat/lon) and zoom level

Security Layers

  1. Filesystem lockfile (config.lock, SSH-only): hard-disables all web config changes regardless of auth state — touch config.lock to lock, rm config.lock to unlock
  2. Flask-Login session auth: single admin account, hashed password in .env
  3. CSRF protection on all POST forms (manual token, no extra dependency)
  4. HTTPS required in production (SESSION_COOKIE_SECURE)

What Lives Where

Data Location
MongoDB credentials .env
Flask secret key .env
Admin password hash .env
Admin username settings.json
Receiver callsign settings.json
Receiver grid square settings.json
Display defaults, bands, map view settings.json
Band colors, freq ranges, zone maps config.js (dev edit only)

Module Structure

config_module/
├── __init__.py         ← register(app) — called optionally via try/except in app.py
├── routes.py           ← Blueprint: GET/POST /settings, GET/POST /login, GET /logout
├── auth.py             ← Flask-Login setup, AdminUser model (no DB)
├── storage.py          ← settings.json R/W, config.lock check
├── templates/config_module/
│   ├── gear_icon.html  ← included by main templates via {% include ... ignore missing %}
│   └── login.html
└── static/
    ├── config_module.js
    └── config_module.css

Minimal Changes to Existing Files

  • app.py: one try/except ImportError block to optionally load module
  • Each template: one line — {% include 'config_module/gear_icon.html' ignore missing %}
  • requirements.txt: add flask-login>=0.6.0
  • .env/.env.example: remove RECEIVER_CALLSIGN/RECEIVER_GRIDSQUARE; add FLASK_SECRET_KEY, ADMIN_PASSWORD_HASH
  • .gitignore: add config.lock, settings.json
  • No changes to existing API or view routes

Acceptance Criteria

  • rm -rf config_module/ leaves app fully functional with default values, no errors
  • config.lock present → POST /settings returns 423; modal is read-only with lock message
  • Unauthenticated gear click → redirect to /login
  • Authenticated → editable modal; save writes settings.json; updates live without restart
  • RECEIVER_CALLSIGN and RECEIVER_GRIDSQUARE removed from .env
  • settings.json and config.lock in .gitignore
  • set_password.py helper script included for generating password hash
  • Only one new line added to each existing template (ignore missing include)
  • No changes to existing API or view routes

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions