From 9bda3ab14b739417650f379518e67787ad6c6a7f Mon Sep 17 00:00:00 2001 From: nmemmert <67961292+nmemmert@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:51:15 -0500 Subject: [PATCH] Add local admin login alongside Entra ID authentication --- ENTRA_SETUP.md | 2 ++ README.md | 20 +++++++++++-- pdf_renamer.py | 78 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/ENTRA_SETUP.md b/ENTRA_SETUP.md index dc26ea3..8a6cea1 100644 --- a/ENTRA_SETUP.md +++ b/ENTRA_SETUP.md @@ -2,6 +2,8 @@ This guide provides step-by-step instructions for setting up Microsoft Entra ID (formerly Azure Active Directory) authentication for the PDF Renamer web application. +**Note**: Entra ID authentication is optional. The application also supports local admin login. Follow this guide only if you want to enable Microsoft Entra ID authentication. + ## Prerequisites - An Azure subscription diff --git a/README.md b/README.md index 3192280..b9f1399 100644 --- a/README.md +++ b/README.md @@ -23,16 +23,32 @@ A Python web application built with Flask that automatically renames PDF files b **Detailed setup instructions**: See [ENTRA_SETUP.md](ENTRA_SETUP.md) for comprehensive step-by-step guide to configure Microsoft Entra ID authentication. ### Quick Setup: -1. Register application in Azure Portal -2. Get Tenant ID, Client ID, and Client Secret +1. Register application in Azure Portal (optional for admin login) +2. Get Tenant ID, Client ID, and Client Secret (for Entra login) 3. Set environment variables: ```bash + # For Entra ID login (optional) export TENANT_ID="your-tenant-id" export CLIENT_ID="your-client-id" export CLIENT_SECRET="your-client-secret" + + # For admin login + export ADMIN_USERNAME="admin" + export ADMIN_PASSWORD="your-secure-password" + + # Required export SECRET_KEY="your-random-secret-key" ``` +## Authentication + +The application supports two authentication methods: + +1. **Microsoft Entra ID**: OAuth2 authentication with Azure AD +2. **Admin Login**: Local username/password authentication + +Users can choose their preferred login method on the login page. + ## Installation 1. **Clone the repository**: diff --git a/pdf_renamer.py b/pdf_renamer.py index 883ae30..bc1c73c 100644 --- a/pdf_renamer.py +++ b/pdf_renamer.py @@ -17,6 +17,10 @@ TENANT_ID = os.getenv('TENANT_ID', 'your-tenant-id') CLIENT_ID = os.getenv('CLIENT_ID', 'your-client-id') CLIENT_SECRET = os.getenv('CLIENT_SECRET', 'your-client-secret') +# Admin Configuration +ADMIN_USERNAME = os.getenv('ADMIN_USERNAME', 'admin') +ADMIN_PASSWORD = os.getenv('ADMIN_PASSWORD', 'change-this-password') + oauth = OAuth(app) microsoft = oauth.register( name='microsoft', @@ -31,7 +35,7 @@ ALLOWED_EXTENSIONS = {'pdf', 'zip'} def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): - if 'user' not in session: + if 'user' not in session and 'admin' not in session: return redirect(url_for('login')) return f(*args, **kwargs) return decorated_function @@ -54,9 +58,80 @@ def extract_names_from_pdf(pdf_path): @app.route('/login') def login(): + return render_template_string(''' + + + + Login + + + + Logo +

PDF Renamer Login

+
+
+

Microsoft Entra ID

+ Login with Microsoft +
+
+

Admin Login

+
+
+
+ +
+
+
+ + + ''') + +@app.route('/entra-login') +def entra_login(): redirect_uri = url_for('auth', _external=True) return microsoft.authorize_redirect(redirect_uri) +@app.route('/admin-login', methods=['POST']) +def admin_login(): + username = request.form.get('username') + password = request.form.get('password') + if username == ADMIN_USERNAME and password == ADMIN_PASSWORD: + session['admin'] = True + return redirect(url_for('upload_files')) + else: + return render_template_string(''' + + + + Login Failed + + + +
+

Login Failed

+

Invalid username or password.

+ Try Again +
+ + + ''') + @app.route('/auth') def auth(): token = microsoft.authorize_access_token() @@ -68,6 +143,7 @@ def auth(): @login_required def logout(): session.pop('user', None) + session.pop('admin', None) return redirect(url_for('login')) @app.route('/', methods=['GET', 'POST'])