diff --git a/motm_app/main.py b/motm_app/main.py index 6c9ecf8..0309a1f 100644 --- a/motm_app/main.py +++ b/motm_app/main.py @@ -24,9 +24,9 @@ from sqlalchemy import text from flask_wtf import FlaskForm from flask_bootstrap import Bootstrap from flask_basicauth import BasicAuth -from wtforms import StringField, PasswordField, BooleanField +from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms import DateField -from wtforms.validators import InputRequired, Email, Length +from wtforms.validators import InputRequired, Email, Length, EqualTo from forms import motmForm, adminSettingsForm2, goalsAssistsForm, DatabaseSetupForm, PlayerForm, ClubForm, TeamForm, DataImportForm, ClubSelectionForm from db_config import sql_write, sql_write_static, sql_read, sql_read_static from sqlalchemy import text @@ -35,9 +35,32 @@ from readSettings import mySettings from fixture_scraper import FixtureScraper, get_next_hkfc_c_fixture, get_opponent_club_name, get_opponent_club_info, match_opponent_to_club from club_scraper import ClubScraper, get_hk_hockey_clubs, expand_club_abbreviation -app.config['BASIC_AUTH_USERNAME'] = 'admin' -app.config['BASIC_AUTH_PASSWORD'] = 'letmein' -basic_auth = BasicAuth(app) +# Custom authentication class that uses database +class DatabaseBasicAuth(BasicAuth): + def check_credentials(self, username, password): + try: + sql = text("SELECT password_hash FROM admin_profiles WHERE username = :username") + result = sql_read(sql, {'username': username}) + if result: + stored_hash = result[0]['password_hash'] + password_hash = hashlib.sha256(password.encode()).hexdigest() + return password_hash == stored_hash + return False + except Exception as e: + print(f"Authentication error: {e}") + return False + +app.config['BASIC_AUTH_USERNAME'] = 'admin' # Fallback for compatibility +app.config['BASIC_AUTH_PASSWORD'] = 'letmein' # Fallback for compatibility +basic_auth = DatabaseBasicAuth(app) + +# Admin profile form +class AdminProfileForm(FlaskForm): + current_password = PasswordField('Current Password', validators=[InputRequired()]) + new_password = PasswordField('New Password', validators=[InputRequired(), Length(min=6)]) + confirm_password = PasswordField('Confirm New Password', validators=[InputRequired(), EqualTo('new_password', message='Passwords must match')]) + email = StringField('Email', validators=[Email()]) + submit = SubmitField('Update Profile') @app.route('/') @@ -53,6 +76,57 @@ def admin_dashboard(): return render_template('admin_dashboard.html') +@app.route('/admin/profile', methods=['GET', 'POST']) +@basic_auth.required +def admin_profile(): + """Admin profile page for changing password and settings""" + form = AdminProfileForm() + + # Get current admin profile + sql = text("SELECT username, email FROM admin_profiles WHERE username = 'admin'") + profile = sql_read(sql) + + if profile: + current_email = profile[0]['email'] + else: + current_email = '' + + if request.method == 'POST' and form.validate_on_submit(): + # Verify current password + sql_check = text("SELECT password_hash FROM admin_profiles WHERE username = 'admin'") + result = sql_read(sql_check) + + if result: + stored_hash = result[0]['password_hash'] + current_password_hash = hashlib.sha256(form.current_password.data.encode()).hexdigest() + + if current_password_hash == stored_hash: + # Update password and email + new_password_hash = hashlib.sha256(form.new_password.data.encode()).hexdigest() + + sql_update = text(""" + UPDATE admin_profiles + SET password_hash = :password_hash, email = :email, updated_at = CURRENT_TIMESTAMP + WHERE username = 'admin' + """) + sql_write(sql_update, { + 'password_hash': new_password_hash, + 'email': form.email.data + }) + + flash('Profile updated successfully!', 'success') + return redirect(url_for('admin_profile')) + else: + flash('Current password is incorrect!', 'error') + else: + flash('Admin profile not found!', 'error') + + # Pre-populate email field + form.email.data = current_email + + return render_template('admin_profile.html', form=form, current_email=current_email) + + # ==================== PUBLIC VOTING SECTION ==================== @app.route('/motm/') diff --git a/motm_app/templates/admin_dashboard.html b/motm_app/templates/admin_dashboard.html index 646f0a0..c16cb20 100644 --- a/motm_app/templates/admin_dashboard.html +++ b/motm_app/templates/admin_dashboard.html @@ -38,6 +38,7 @@
Back to Main Page + Admin Profile
diff --git a/motm_app/templates/admin_profile.html b/motm_app/templates/admin_profile.html new file mode 100644 index 0000000..f64bcab --- /dev/null +++ b/motm_app/templates/admin_profile.html @@ -0,0 +1,160 @@ + + + + + + Admin Profile - HKFC Men's C Team MOTM System + + + + +
+
+
+

Admin Profile

+

Manage your admin account settings and password

+ + + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + + +
+
Security Notice
+

+ Important: Changing your password will immediately affect access to all admin functions. + Make sure to remember your new password or store it securely. +

+
+ + +
+

Change Password & Settings

+
+ {{ form.hidden_tag() }} + +
+
+
+ {{ form.current_password.label(class="form-label") }} + {{ form.current_password(class="form-control", placeholder="Enter current password") }} + {% if form.current_password.errors %} +
+ {% for error in form.current_password.errors %} + {{ error }} + {% endfor %} +
+ {% endif %} +
+
+
+ +
+
+
+ {{ form.new_password.label(class="form-label") }} + {{ form.new_password(class="form-control", placeholder="Enter new password (min 6 characters)") }} + {% if form.new_password.errors %} +
+ {% for error in form.new_password.errors %} + {{ error }} + {% endfor %} +
+ {% endif %} +
+
+
+
+ {{ form.confirm_password.label(class="form-label") }} + {{ form.confirm_password(class="form-control", placeholder="Confirm new password") }} + {% if form.confirm_password.errors %} +
+ {% for error in form.confirm_password.errors %} + {{ error }} + {% endfor %} +
+ {% endif %} +
+
+
+ +
+
+
+ {{ form.email.label(class="form-label") }} + {{ form.email(class="form-control", placeholder="admin@example.com") }} + {% if form.email.errors %} +
+ {% for error in form.email.errors %} + {{ error }} + {% endfor %} +
+ {% endif %} +
+
+
+ +
+ + Cancel +
+
+
+ + +
+

Current Profile Information

+
+
+

Username: admin

+

Email: {{ current_email or 'Not set' }}

+
+
+

Account Type: Administrator

+

Access Level: Full Admin Rights

+
+
+
+ + +
+

Password Requirements

+
    +
  • Minimum 6 characters
  • +
  • Must match confirmation
  • +
  • Current password must be correct
  • +
+
+
+
+
+ + + +