From 884cc0e69407f9dd151c1644ec81889716eb01c7 Mon Sep 17 00:00:00 2001 From: Ethan-clay03 <ethanclay2017@gmail.com> Date: Tue, 7 Jan 2025 18:09:34 +0000 Subject: [PATCH] Fix super admin being unable to access admin pages, begin to move admin to separate bp route and work on adding bookings via UI --- app/__init__.py | 29 +++++++++++--------- app/bookings/routes.py | 2 -- app/logger/__init__.py | 2 +- app/profile/routes.py | 16 +++++++++-- app/templates/admin/index.html | 44 +++++++++++++++++++++++++++++++ app/templates/admin/listings.html | 12 +++++++++ app/templates/base.html | 3 +++ 7 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 app/templates/admin/index.html create mode 100644 app/templates/admin/listings.html diff --git a/app/__init__.py b/app/__init__.py index be141ad..7daa6ca 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -6,6 +6,7 @@ from flask_login import LoginManager, current_user from flask_principal import Principal, Permission, RoleNeed, identity_loaded from flask_wtf.csrf import CSRFProtect from dotenv import load_dotenv +from app.logger import auth_logger from functools import wraps import os @@ -21,7 +22,7 @@ def permission_required(permission): @wraps(f) def decorated_function(*args, **kwargs): if not permission.can(): - current_app.logger.debug(f'Permission denied for {current_user} attempting to access {request.endpoint}.') + auth_logger.debug(f'Permission denied for {current_user} attempting to access {request.endpoint}.') abort(403) return f(*args, **kwargs) return decorated_function @@ -50,6 +51,7 @@ def create_app(config_class=Config): app.config['SECRET_KEY'] = os.getenv('SECRET_KEY') or 'your_secret_key' app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{db_user}:{db_password}@{db_host}/{db_name}" + # Initialize extensions with the app db.init_app(app) migrate.init_app(app, db) login_manager.init_app(app) @@ -69,11 +71,10 @@ def create_app(config_class=Config): if user and user.role: identity.provides.add(RoleNeed(user.role.name)) if user.role.name == 'super-admin': - identity.provides.add(RoleNeed('admin')) - identity.provides.add(RoleNeed('super-admin')) + identity.provides.add(RoleNeed('admin')) # Super admin should also inherit admin permissions + auth_logger.debug(f'Roles provided to identity for {current_user}: {identity.provides}') else: - current_app.logger.debug(f'No role found for user {identity.user.username}.') - + auth_logger.debug(f'No role found for user {identity.user.username}.') # Add global template variables @@ -102,27 +103,31 @@ def create_app(config_class=Config): @app.before_request def before_request(): - g.admin_permission = admin_permission - g.user_permission = user_permission - g.super_admin_permission = super_admin_permission + g.admin_permission = None + g.user_permission = None + g.super_admin_permission = None + g.is_admin = False + g.is_super_admin = False if current_user.is_authenticated: role = current_user.role if role: + g.user_permission = user_permission if role.name == 'super-admin': g.super_admin_permission = super_admin_permission g.admin_permission = admin_permission - g.user_permission = user_permission + g.is_super_admin = True + g.is_admin = True elif role.name == 'admin': g.admin_permission = admin_permission - g.user_permission = user_permission - elif role.name == 'user': - g.user_permission = user_permission + g.is_admin = True + auth_logger.debug(f'Permissions for {current_user}: Admin: {g.admin_permission}, User: {g.user_permission}, Super Admin: {g.super_admin_permission}') login_manager.login_view = 'profile.login' return app + @login_manager.user_loader def load_user(user_id): from app.models import User diff --git a/app/bookings/routes.py b/app/bookings/routes.py index a340d57..998d5e7 100644 --- a/app/bookings/routes.py +++ b/app/bookings/routes.py @@ -1,10 +1,8 @@ from flask import render_template, redirect, url_for, g from app.bookings import bp from app.models import Listings, ListingImages -from app import admin_permission, permission_required, user_permission, super_admin_permission @bp.route('/home') -@permission_required(user_permission) def index(): listing_ids = [] top_listings = Listings.get_top_listings(5) diff --git a/app/logger/__init__.py b/app/logger/__init__.py index 31350b1..8c8e658 100644 --- a/app/logger/__init__.py +++ b/app/logger/__init__.py @@ -35,4 +35,4 @@ class LoggerConfig: logger_config = LoggerConfig() app_logger = logger_config.setup_logger('app', log_file='app.log', level=LOG_LEVELS['debug']) db_logger = logger_config.setup_logger('db', log_file='db.log', level=LOG_LEVELS['info']) -auth_logger = logger_config.setup_logger('auth', log_file='auth.log', level=LOG_LEVELS['warning']) +auth_logger = logger_config.setup_logger('auth', log_file='auth.log', level=LOG_LEVELS['debug']) diff --git a/app/profile/routes.py b/app/profile/routes.py index 4b04a1a..2678dcb 100644 --- a/app/profile/routes.py +++ b/app/profile/routes.py @@ -1,13 +1,14 @@ #https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login#step-1-installing-packages -from flask import render_template, redirect, url_for, request, flash, jsonify, session, current_app +from flask import render_template, redirect, url_for, request, flash, jsonify, session, current_app, abort from flask_principal import Identity, identity_changed from flask_login import login_user, logout_user, login_required, current_user from werkzeug.security import check_password_hash from app.profile import bp from app.models import User from app.logger import auth_logger +from app import admin_permission, permission_required, super_admin_permission from app import db - +from flask_principal import Principal, Permission, RoleNeed, identity_loaded @bp.route('/signup', methods=['GET', 'POST']) def signup(): @@ -86,6 +87,10 @@ def login_post(): return redirect(url_for('profile.index')) +@bp.route('/admin/index') +@permission_required(admin_permission) +def admin_index(): + return render_template('profile/admin-index.html') @bp.route('/check-username', methods=['POST']) @@ -127,6 +132,7 @@ def logout(): logout_user() return redirect(url_for('main.index')) + @bp.route('/login') def login(): if current_user.is_authenticated: @@ -135,10 +141,12 @@ def login(): error = request.args.get('error') return render_template('profile/login.html', error=error) + @bp.route('/password-reset') def password_reset(): return render_template('profile/password-reset.html') + @bp.route('/password-reset', methods=['POST']) def check_password_reset_1(): username = request.form.get('username') @@ -156,10 +164,12 @@ def check_password_reset_1(): session['password-reset-email'] = email return redirect(url_for('profile.password_reset_2')) + @bp.route('/password-reset/2FA') def password_reset_2(): return render_template('profile/password-reset-2.html') + @bp.route('/password-reset/2FA', methods=['POST']) def check_password_reset_2(): code = request.form.get('2fa-code') @@ -170,10 +180,12 @@ def check_password_reset_2(): return flash('Invalid 2FA Code') + @bp.route('/password-reset/reset-password') def password_reset_3(): return render_template('profile/password-reset-3.html') + @bp.route('/password-reset/reset-password', methods=['POST']) def password_reset_process(): email = session.get('email') diff --git a/app/templates/admin/index.html b/app/templates/admin/index.html new file mode 100644 index 0000000..511b121 --- /dev/null +++ b/app/templates/admin/index.html @@ -0,0 +1,44 @@ +{% extends 'base.html' %} + +{% block content %} +<head> + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='listings.css')}}"> + <script src="https://cdn.jsdelivr.net/npm/swiffy-slider@1.6.0/dist/js/swiffy-slider.min.js" crossorigin="anonymous" defer></script> + <link href="https://cdn.jsdelivr.net/npm/swiffy-slider@1.6.0/dist/css/swiffy-slider.min.css" rel="stylesheet" crossorigin="anonymous"> +</head> +<div class="content"> + <div class="deals_text"><span class="deals_underline">Deals for you</span></div> + <div class="swiffy-slider slider-item-show2 slider-item-reveal slider-nav-outside slider-nav-round slider-nav-visible slider-indicators-outside slider-indicators-round slider-indicators-dark slider-nav-animation slider-nav-animation-fadein slider-item-first-visible"> + <ul class="slider-container py-4"> + {% for listing in top_listings %} + <li class="slide-visible"> + <div class="card shadow h-100"> + <div class="ratio ratio-16x9"> + <th>{{top_listing_images[listing.id]}}</th> + <img src="{{ url_for('main.upload_file', filename=top_listing_images[listing.id]) }}" class="card-img-top" loading="lazy" alt="Main Image"> + </div> + <div class="card-body p-3 p-xl-4"> + <h3 class="card-title h5">{{listing.destination_location}}</h3> + <p class="card-text">Add Location description here once implemented</p> + <div><a href="#" class="btn btn-primary">Book now</a> + </div> + </div> + </div> + </li> + {% endfor %} + </ul> + + <button type="button" class="slider-nav" aria-label="Go left"></button> + <button type="button" class="slider-nav slider-nav-next" aria-label="Go left"></button> + + <div class="slider-indicators"> + <button class="active" aria-label="Go to slide"></button> + <button aria-label="Go to slide" class=""></button> + <button aria-label="Go to slide" class=""></button> + <button aria-label="Go to slide" class=""></button> + </div> + </div> +</div> + +<div style="margin-left:50px"> <a href= "{{ url_for('bookings.listings') }}" class="button_1">View All Deals</a></div> +{% endblock %} \ No newline at end of file diff --git a/app/templates/admin/listings.html b/app/templates/admin/listings.html new file mode 100644 index 0000000..5fba48d --- /dev/null +++ b/app/templates/admin/listings.html @@ -0,0 +1,12 @@ +{% extends 'base.html' %} +# Implements CSS Slider from https://swiffyslider.com/docs/ +{% block content %} + <head> + <!-- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='listings.css')}}"> --> + <script src="https://cdn.jsdelivr.net/npm/swiffy-slider@1.6.0/dist/js/swiffy-slider.min.js" crossorigin="anonymous" defer></script> + <link href="https://cdn.jsdelivr.net/npm/swiffy-slider@1.6.0/dist/css/swiffy-slider.min.css" rel="stylesheet" crossorigin="anonymous"> + </head> + <div class="content"> + <p>Create full listings page here with search functionality</p> + </div> +{% endblock %} \ No newline at end of file diff --git a/app/templates/base.html b/app/templates/base.html index 5f4c7ab..e2c7912 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -39,6 +39,9 @@ </a> <ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink"> {% if user_in_session %} + {% if g.is_admin %} + <li><a class="dropdown-item" href="{{ url_for('profile.admin_index') }}">Admin Options</a></li> + {% endif %} <li><a class="dropdown-item" href="{{ url_for('profile.index') }}">Account Details</a></li> <li><a class="dropdown-item" href="{{ url_for('profile.manage_bookings')}}">My Bookings</a></li> <li><a class="dropdown-item" href="{{ url_for('profile.logout') }}">Log Out</a></li> -- GitLab