From f4edf4026b7a4331b856dc28c9821dabd011147b Mon Sep 17 00:00:00 2001 From: Ethan-clay03 <ethanclay2017@gmail.com> Date: Mon, 16 Dec 2024 18:14:02 +0000 Subject: [PATCH] Adde debugging locally, working on login/signup pages, to begin work on RBAC --- .dockerignore | 27 +++++++++++ .idea/.gitignore | 3 ++ .idea/Flask.iml | 14 ++++++ .../inspectionProfiles/profiles_settings.xml | 6 +++ .idea/misc.xml | 4 ++ .idea/modules.xml | 8 ++++ .idea/vcs.xml | 6 +++ .vscode/launch.json | 25 ++++++++++ Dockerfile | 30 +++++++++--- app/profile/routes.py | 31 +++++++++---- app/static/base.css | 30 ++++++++++++ app/templates/profile/login.html | 41 +++++++++-------- app/templates/profile/signup.html | 41 ++++++++--------- app/uploads/listing_images/2.jpg | Bin 0 -> 25747 bytes docker-compose.debug.yml | 43 ++++++++++++++++++ docker-compose.yml | 10 ++++ requirements.txt | 3 +- 17 files changed, 263 insertions(+), 59 deletions(-) create mode 100644 .dockerignore create mode 100644 .idea/.gitignore create mode 100644 .idea/Flask.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .vscode/launch.json create mode 100644 app/uploads/listing_images/2.jpg create mode 100644 docker-compose.debug.yml create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e0cc78f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,27 @@ +**/__pycache__ +**/.venv +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/Flask.iml b/.idea/Flask.iml new file mode 100644 index 0000000..f5d4576 --- /dev/null +++ b/.idea/Flask.iml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="PYTHON_MODULE" version="4"> + <component name="NewModuleRootManager"> + <content url="file://$MODULE_DIR$"> + <excludeFolder url="file://$MODULE_DIR$/.venv" /> + </content> + <orderEntry type="jdk" jdkName="Python 3.10 (Flask)" jdkType="Python SDK" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> + <component name="PyDocumentationSettings"> + <option name="format" value="PLAIN" /> + <option name="myDocStringFormat" value="Plain" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ +<component name="InspectionProjectProfileManager"> + <settings> + <option name="USE_PROJECT_PROFILE" value="false" /> + <version value="1.0" /> + </settings> +</component> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..bbe7b36 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (Flask)" project-jdk-type="Python SDK" /> +</project> \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..71b84bc --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/Flask.iml" filepath="$PROJECT_DIR$/.idea/Flask.iml" /> + </modules> + </component> +</project> \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..68bf174 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Flask", + "type": "debugpy", + "request": "launch", + "module": "flask", + "env": { + "FLASK_APP": "app\\__init__.py", + "FLASK_DEBUG": "1" + }, + "args": [ + "run", + "--no-debugger", + "--no-reload" + ], + "jinja": true, + "autoStartBrowser": false + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4f38856..ed09238 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,26 @@ -FROM python:3.10-alpine AS builder +# For more information, please refer to https://aka.ms/vscode-docker-python +FROM python:3-slim -COPY . /flask -WORKDIR ./flask +EXPOSE 5000 -RUN pip3 install --no-cache-dir -r requirements.txt +# Keeps Python from generating .pyc files in the container +ENV PYTHONDONTWRITEBYTECODE=1 + +# Turns off buffering for easier container logging +ENV PYTHONUNBUFFERED=1 + +# Install pip requirements +COPY requirements.txt . +RUN python -m pip install -r requirements.txt + +WORKDIR /app +COPY . /app + +# Creates a non-root user with an explicit UID and adds permission to access the /app folder +# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers +RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app +USER appuser + +# During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug +CMD ["flask", "run", "--host=0.0.0.0", "--port=5000"] -ENV USE_RELOADER=true -ENV FLASK_APP=app:create_app -CMD ["flask", "run", "--host=0.0.0.0", "--port=5000"] \ No newline at end of file diff --git a/app/profile/routes.py b/app/profile/routes.py index 353072a..63f67bb 100644 --- a/app/profile/routes.py +++ b/app/profile/routes.py @@ -1,38 +1,51 @@ #https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login#step-1-installing-packages from flask import Blueprint, render_template, redirect, url_for, request, flash from app.profile import bp -from werkzeug.security import generate_password_hash, check_password_hash +from werkzeug.security import check_password_hash from app.models import User from app import db from flask_login import login_user, logout_user, login_required, current_user +from app.logger import auth_logger @bp.route('/signup') def signup(): - return render_template(url_for('profile.signup')) + return render_template('profile/signup.html') @bp.route('/signup', methods=['POST']) def signup_post(): - email = request.form.get('email') - username = request.form.get('name') - password = request.form.get('password') + form_data = { + 'email': request.form.get('email'), + 'username': request.form.get('username'), + 'password': request.form.get('password') + } - user = User.query.filter_by(email=email).first() + user = User.query.filter_by(email=form_data['email']).first() if user: return redirect(url_for('profile.signup')) - new_user = User.create_user(username=username, email=email, password=password) + try: + if not form_data['username'] or not form_data['email'] or not form_data['password']: + raise ValueError("Missing required fields: Email, Username, Password") + new_user = User.create_user(username=form_data['username'], email=form_data['email'], password=form_data['password']) + except ValueError as e: + auth_logger.error("Unable to create user, full error was: " + str(e)) + flash('Missing required fields') + return redirect(url_for('profile.signup')) db.session.add(new_user) db.session.commit() - return redirect(url_for('profile.login')) + # Log the user in automatically + login_user(new_user) + + return redirect(url_for('profile.index')) @bp.route('/login', methods=['POST']) def login_post(): email = request.form.get('email') password = request.form.get('password') - remember = True if request.form.get('remember') else False # If checkbox for user is ticked + remember = True if request.form.get('remember') else False # Change to boolean value depending if tickbox is selected user = User.query.filter_by(email=email).first() diff --git a/app/static/base.css b/app/static/base.css index fa23b77..008b2e0 100644 --- a/app/static/base.css +++ b/app/static/base.css @@ -82,8 +82,38 @@ body { background-color: #90AEAD; } +/** +Form styling options +*/ + +.form_box_30 { + position: relative; + width: 100%; + max-width: 400px; /* Maximum width for larger screens */ + margin: auto; + vertical-align: middle; + padding: 15px; /* Add some padding */ +} + +@media (max-width: 600px) { + .form_box_30 { + width: 90%; /* Adjust width for smaller screens */ + } +} + +.background_1 { + border-radius: 25px; + background: #90AEAD; + padding: 50px; + color: #FBE9D0; + background-image: linear-gradient(to bottom right, #4169e1, #89CFF0); +} +.form-check-input:checked { + color: #FBE9D0; + background: #FBE9D0; +} /* HEX codes for colours used through out website don't delete */ diff --git a/app/templates/profile/login.html b/app/templates/profile/login.html index 2fb3775..130d7ef 100644 --- a/app/templates/profile/login.html +++ b/app/templates/profile/login.html @@ -1,28 +1,29 @@ {% extends 'base.html' %} {% block content %} <div class="column is-4 is-offset-4"> - <h3 class="title">Login</h3> - <div class="box"> - <form method="POST" action="{{ url_for ('profile.login_post')}}"> - <div class="field"> - <div class="control"> - <input class="input is-large" type="email" name="email" placeholder="Your Email" autofocus=""> + <div id="login-box" class="form_box_30"> + <h3 style="margin-top:30px; text-align:center">Login</h3> + <div class="background_1"> + <form method="POST" action="{{ url_for('profile.login_post') }}"> + <div class="mb-3"> + <label class="form-label" autofocus="">Username</label> + <input type="text" class="form-control" name="username"> </div> - </div> - - <div class="field"> - <div class="control"> - <input class="input is-large" type="password" name="password" placeholder="Your Password"> + <div class="mb-3"> + <label class="form-label">Email address</label> + <input type="email" class="form-control" name="email"> </div> - </div> - <div class="field"> - <label class="checkbox"> - <input type="checkbox" name="remember"> - Remember me - </label> - </div> - <button class="button is-block is-info is-large is-fullwidth">Login</button> - </form> + <div class="mb-3"> + <label for="exampleInputPassword1" class="form-label">Password</label> + <input type="password" class="form-control" name="password"> + </div> + <div class="mb-3 form-check"> + <input type="checkbox" class="form-check-input" id="remember" name="remember"> + <label class="form-check-label" for="remember">Remember me</label> + </div> + <button type="submit" class="btn btn-primary">Log In</button> + </form> + </div> </div> </div> {% endblock %} \ No newline at end of file diff --git a/app/templates/profile/signup.html b/app/templates/profile/signup.html index 8966ed1..a7ef4e8 100644 --- a/app/templates/profile/signup.html +++ b/app/templates/profile/signup.html @@ -1,30 +1,27 @@ {% extends 'base.html' %} {% block content %} -<div class="column is-4 is-offset-4"> - <h3 class="title">Sign Up</h3> - <div class="box"> - <form method="POST" action="/signup"> - <div class="field"> - <div class="control"> - <input class="input is-large" type="email" name="email" placeholder="Email" autofocus=""> - </div> + <div class="form_box_30 background_1"> + <h3 style="margin-top:30px">Sign Up</h3> + <form method="POST" action="{{ url_for('profile.signup_post') }}"> + <div class="mb-3"> + <label class="form-label" autofocus="">Username</label> + <input type="username" class="form-control" name="username"> </div> - - <div class="field"> - <div class="control"> - <input class="input is-large" type="text" name="name" placeholder="Name" autofocus=""> - </div> + <div class="mb-3"> + <label class="form-label">Email address</label> + <input type="email" class="form-control" name="email"> </div> - - <div class="field"> - <div class="control"> - <input class="input is-large" type="password" name="password" placeholder="Password"> - </div> + <div class="mb-3"> + <label class="form-label">Password</label> + <input type="password" class="form-control" name="password"> </div> - - <button class="button is-block is-info is-large is-fullwidth">Sign Up</button> - </form> + <div class="mb-3 form-check"> + <input type="checkbox" class="form-check-input" id="remember" name="remember"> + <label class="form-check-label" for="remember">Remember me</label> + </div> + <button type="submit" class="btn btn-primary">Log In</button> + </form> </div> -</div> + {% endblock %} \ No newline at end of file diff --git a/app/uploads/listing_images/2.jpg b/app/uploads/listing_images/2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e0a1c1206a55aa6127782399d2eb8bfb401f7029 GIT binary patch literal 25747 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 literal 0 HcmV?d00001 diff --git a/docker-compose.debug.yml b/docker-compose.debug.yml new file mode 100644 index 0000000..37266e1 --- /dev/null +++ b/docker-compose.debug.yml @@ -0,0 +1,43 @@ +version: '3.4' + +services: + flask: + image: flask + build: + context: . + dockerfile: ./Dockerfile + command: ["sh", "-c", "pip install debugpy -t /tmp && python /tmp/debugpy --wait-for-client --listen 0.0.0.0:5678 -m flask run --no-debugger --no-reload --host 0.0.0.0 --port 5000"] + ports: + - 5000:5000 + - 5678:5678 + depends_on: + - database + networks: + - network + environment: + DEVELOPMENT_MODE: ${DEVELOPMENT_MODE} + DATABASE_HOST: ${DATABASE_HOST} + DATABASE_USER: ${DATABASE_USER} + DATABASE_PASSWORD: ${DATABASE_PASSWORD} + DATABASE_NAME: ${DATABASE_NAME} + FLASK_APP: app\__init__.py + database: + image: mysql:8.0 + ports: + - '3306:3306' + environment: + MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD} + MYSQL_DATABASE: ${DATABASE_NAME} + MYSQL_USER: ${DATABASE_USER} + MYSQL_PASSWORD: ${DATABASE_PASSWORD} + volumes: + - mysql_data:/var/lib/mysql + networks: + - network + +volumes: + mysql_data: + +networks: + network: + driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f028cec --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.4' + +services: + flask: + image: flask + build: + context: . + dockerfile: ./Dockerfile + ports: + - 5000:5000 diff --git a/requirements.txt b/requirements.txt index 4f3f85f..6193046 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ pymysql python-dotenv jinja2 cryptography -flask-login \ No newline at end of file +flask-login +debugpy \ No newline at end of file -- GitLab