Skip to content

Commit

Permalink
updated dependencies migrates all auth to flask jwt extended
Browse files Browse the repository at this point in the history
  • Loading branch information
Snickdx committed Feb 25, 2024
1 parent 99a28d9 commit 0a4157d
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 96 deletions.
15 changes: 11 additions & 4 deletions .gitpod.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
FROM gitpod/workspace-full

USER root
RUN sudo apt-get update
RUN sudo apt-get install -y libgbm-dev gconf-service libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxss1 libxtst6 libappindicator1 libnss3 libasound2 libatk1.0-0 libc6 ca-certificates fonts-liberation lsb-release xdg-utils wget

USER gitpod

# Copy the .python-version file into the Docker image to use it during the build
COPY .python-version /home/gitpod/.python-version

# Install the specific Python version from .python-version and upgrade pip
RUN pyenv install $(cat /home/gitpod/.python-version) && \
pyenv global $(cat /home/gitpod/.python-version) && \
eval "$(pyenv init -)" && \
pip install --upgrade pip
2 changes: 1 addition & 1 deletion .gitpod.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
tasks:
- init: pip install -r requirements.txt & pyenv install
- init: pip install -r requirements.txt
command: flask init & flask run

ports:
Expand Down
63 changes: 31 additions & 32 deletions App/controllers/auth.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,42 @@
from flask_login import login_user, login_manager, logout_user, LoginManager
from flask_jwt_extended import create_access_token, jwt_required, JWTManager
from flask_jwt_extended import create_access_token, jwt_required, JWTManager, get_jwt_identity, verify_jwt_in_request

from App.models import User

def jwt_authenticate(username, password):
def login(username, password):
user = User.query.filter_by(username=username).first()
if user and user.check_password(password):
return create_access_token(identity=username)
return None

def login(username, password):
user = User.query.filter_by(username=username).first()
if user and user.check_password(password):
return user
return None

def setup_flask_login(app):
login_manager = LoginManager()
login_manager.init_app(app)

@login_manager.user_loader
def load_user(user_id):
return User.query.get(user_id)

return login_manager

def setup_jwt(app):
jwt = JWTManager(app)

@jwt.user_identity_loader
def user_identity_lookup(identity):
user = User.query.filter_by(username=identity).one_or_none()
if user:
return user.id
return None

@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
identity = jwt_data["sub"]
return User.query.get(identity)
jwt = JWTManager(app)

# configure's flask jwt to resolve get_current_identity() to the corresponding user's ID
@jwt.user_identity_loader
def user_identity_lookup(identity):
user = User.query.filter_by(username=identity).one_or_none()
if user:
return user.id
return None

return jwt
@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
identity = jwt_data["sub"]
return User.query.get(identity)
return jwt

# Context processor to make 'is_authenticated' available to all templates
def add_auth_context(app):
@app.context_processor
def inject_user():
try:
verify_jwt_in_request()
user_id = get_jwt_identity()
current_user = User.query.get(user_id)
is_authenticated = True
except Exception as e:
print(e)
is_authenticated = False
current_user = None
return dict(is_authenticated=is_authenticated, current_user=current_user)
10 changes: 8 additions & 2 deletions App/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from App.controllers import (
setup_jwt,
setup_flask_login
add_auth_context
)

from App.views import views
Expand All @@ -36,12 +36,18 @@ def create_app(config_overrides={}):
app.config['SEVER_NAME'] = '0.0.0.0'
app.config['PREFERRED_URL_SCHEME'] = 'https'
app.config['UPLOADED_PHOTOS_DEST'] = "App/uploads"
app.config['JWT_ACCESS_COOKIE_NAME'] = 'access_token'
app.config["JWT_TOKEN_LOCATION"] = ["cookies", "headers"]
app.config["JWT_COOKIE_SECURE"] = True
app.config["JWT_SECRET_KEY"] = "super-secret"
app.config["JWT_COOKIE_CSRF_PROTECT"] = False
CORS(app)
add_auth_context(app)
photos = UploadSet('photos', TEXT + DOCUMENTS + IMAGES)
configure_uploads(app, photos)
add_views(app)
init_db(app)
setup_jwt(app)
setup_flask_login(app)

app.app_context().push()
return app
5 changes: 2 additions & 3 deletions App/models/user.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from werkzeug.security import check_password_hash, generate_password_hash
from flask_login import UserMixin
from App.database import db

class User(db.Model, UserMixin):
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, nullable=False, unique=True)
password = db.Column(db.String(120), nullable=False)
Expand All @@ -19,7 +18,7 @@ def get_json(self):

def set_password(self, password):
"""Create hashed password."""
self.password = generate_password_hash(password, method='sha256')
self.password = generate_password_hash(password)

def check_password(self, password):
"""Check hashed password."""
Expand Down
3 changes: 3 additions & 0 deletions App/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@

{% block content %}
<h1>Flask MVC</h1>
{% if is_authenticated %}
<p> Welcome {{current_user.username}} </p>
{% endif %}
<p>This is a boileplate flask application which follows the MVC pattern for structuring the project.</p>
{% endblock %}
48 changes: 36 additions & 12 deletions App/templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,42 @@

</head>
<body>
<nav class="purple">
<div class="nav-wrapper">
<a href="#!" class="brand-logo center">{% block page %}{% endblock %}</a>
<div class="nav-wrapper">
<ul id="nav-mobile" class="left">
<li><a href="/">Home</a></li>
<li><a href="/users">Users Jinja</a></li>
<li><a href="/static/users">Users JS</a></li>
</ul>
</div>
</div>
</nav>

<nav class="purple">
<div class="nav-wrapper">
<a href="#!" class="brand-logo center">{% block page %}{% endblock %}</a>
<ul id="nav-mobile" class="left">
<li><a href="/">Home</a></li>
<li><a href="/users">Users Jinja</a></li>
<li><a href="/identify">Identify</a></li>
<li><a href="/static/users">Users JS</a></li>
</ul>
{% if is_authenticated %}
<ul id="nav-mobile" class="right">
<li><a href="/logout">Logout</a></li>
</ul>
{% else %}
<form class="right navbar-form" method="POST" action="/login" style="display: flex; flex-wrap: nowrap; align-items: center; margin-right: 10px;">
<div class="input-field" style="margin-right: 10px;">
<input placeholder="username" value="bob" name="username" type="text" class="validate" required>
<label for="username">username</label>
</div>
<div class="input-field" style="margin-right: 10px;">
<input placeholder="password"value="bobpass" name="password" type="password" class="validate" required>
<label for="password">Password</label>
</div>
<button type="submit" class="btn waves-effect waves-light">Login</button>
</form>
{% endif %}

</div>
</nav>
<style>
.navbar-form label {
color: white !important;
}
</style>


{% with messages = get_flashed_messages() %}
{% if messages %}
Expand Down
13 changes: 13 additions & 0 deletions App/templates/message.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% extends "layout.html" %}
{% block title %}{{title}}{% endblock %}
{% block page %}{{title}}{% endblock %}

{{ super() }}

{% block content %}
<div class="row">
<div class="container">
{{message}}
</div>
</div>
{% endblock %}
66 changes: 33 additions & 33 deletions App/views/auth.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
from flask import Blueprint, render_template, jsonify, request, send_from_directory, flash, redirect, url_for
from flask_jwt_extended import jwt_required, current_user as jwt_current_user
from flask_login import login_required, login_user, current_user, logout_user
from flask import Blueprint, render_template, jsonify, request, flash, send_from_directory, flash, redirect, url_for
from flask_jwt_extended import jwt_required, current_user, unset_jwt_cookies, set_access_cookies

from.index import index_views

from App.controllers import (
create_user,
jwt_authenticate,
login
login
)

auth_views = Blueprint('auth_views', __name__, template_folder='../templates')




'''
Page/Action Routes
'''
Expand All @@ -21,52 +21,52 @@ def get_user_page():
users = get_all_users()
return render_template('users.html', users=users)


@auth_views.route('/identify', methods=['GET'])
@login_required
@jwt_required()
def identify_page():
return jsonify({'message': f"username: {current_user.username}, id : {current_user.id}"})

return render_template('message.html', title="Identify", message=f"You are logged in as {current_user.id} - {current_user.username}")

@auth_views.route('/login', methods=['POST'])
def login_action():
data = request.form
user = login(data['username'], data['password'])
if user:
login_user(user)
return 'user logged in!'
return 'bad username or password given', 401
token = login(data['username'], data['password'])
response = redirect(request.referrer)
if not token:
flash('Bad username or password given'), 401
else:
flash('Login Successful')
set_access_cookies(response, token)
return response

@auth_views.route('/logout', methods=['GET'])
def logout_action():
data = request.form
user = login(data['username'], data['password'])
return 'logged out!'
response = redirect(request.referrer)
flash("Logged Out!")
unset_jwt_cookies(response)
return response

'''
API Routes
'''

@auth_views.route('/api/users', methods=['GET'])
def get_users_action():
users = get_all_users_json()
return jsonify(users)

@auth_views.route('/api/users', methods=['POST'])
def create_user_endpoint():
data = request.json
create_user(data['username'], data['password'])
return jsonify({'message': f"user {data['username']} created"})

@auth_views.route('/api/login', methods=['POST'])
def user_login_api():
data = request.json
token = jwt_authenticate(data['username'], data['password'])
token = login(data['username'], data['password'])
if not token:
return jsonify(message='bad username or password given'), 401
return jsonify(access_token=token)
response = jsonify(access_token=token)
set_access_cookies(response, token)
return response

@auth_views.route('/api/identify', methods=['GET'])
@jwt_required()
def identify_user_action():
return jsonify({'message': f"username: {jwt_current_user.username}, id : {jwt_current_user.id}"})
def identify_user():
return jsonify({'message': f"username: {current_user.username}, id : {current_user.id}"})

@auth_views.route('/api/logout', methods=['GET'])
def logout_api():
response = jsonify(message="Logged Out!")
unset_jwt_cookies(response)
return response
15 changes: 7 additions & 8 deletions App/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from App.controllers import (
create_user,
jwt_authenticate,
get_all_users,
get_all_users_json,
jwt_required
Expand All @@ -19,6 +18,13 @@ def get_user_page():
users = get_all_users()
return render_template('users.html', users=users)

@user_views.route('/users', methods=['POST'])
def create_user_action():
data = request.form
flash(f"User {data['username']} created!")
create_user(data['username'], data['password'])
return redirect(url_for('user_views.get_user_page'))

@user_views.route('/api/users', methods=['GET'])
def get_users_action():
users = get_all_users_json()
Expand All @@ -30,13 +36,6 @@ def create_user_endpoint():
create_user(data['username'], data['password'])
return jsonify({'message': f"user {data['username']} created"})

@user_views.route('/users', methods=['POST'])
def create_user_action():
data = request.form
flash(f"User {data['username']} created!")
create_user(data['username'], data['password'])
return redirect(url_for('user_views.get_user_page'))

@user_views.route('/static/users', methods=['GET'])
def static_user_page():
return send_from_directory('static', 'static-user.html')
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ greenlet==2.0.2
gunicorn==20.1.0
itsdangerous==2.1.2
python-dotenv==0.21.1
Flask-Login==0.6.2
Flask-Migrate==3.1.0
Flask-Reuploaded==1.2.0
psycopg2-binary==2.9.3
Expand Down

0 comments on commit 0a4157d

Please sign in to comment.