Skip to content

Commit 7cd0988

Browse files
author
App Generator
committed
Bump Codebase Version
1 parent 977a2d2 commit 7cd0988

File tree

1,304 files changed

+60124
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,304 files changed

+60124
-0
lines changed

.dockerignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.git
2+
__pycache__
3+
*.pyc
4+
*.pyo
5+
*.pyd

.env

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
DEBUG=True
2+
SECRET_KEY=S3cr3t_K#Key
3+
DB_ENGINE=postgresql
4+
DB_NAME=appseed-flask
5+
DB_HOST=localhost
6+
DB_PORT=5432
7+
DB_USERNAME=appseed
8+
DB_PASS=pass

.gitignore

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
5+
# tests and coverage
6+
*.pytest_cache
7+
.coverage
8+
9+
# database & logs
10+
*.db
11+
*.sqlite3
12+
*.log
13+
14+
# venv
15+
env
16+
venv
17+
18+
# other
19+
.DS_Store
20+
21+
# sphinx docs
22+
_build
23+
_static
24+
_templates
25+
26+
# javascript
27+
package-lock.json
28+
.vscode/symbols.json
29+
30+
apps/static/assets/node_modules
31+
apps/static/assets/yarn.lock
32+
apps/static/assets/.temp
33+

Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM python:3.9
2+
3+
COPY . .
4+
5+
# set environment variables
6+
ENV PYTHONDONTWRITEBYTECODE 1
7+
ENV PYTHONUNBUFFERED 1
8+
9+
# install python dependencies
10+
RUN pip install --upgrade pip
11+
RUN pip install --no-cache-dir -r requirements.txt
12+
13+
# gunicorn
14+
CMD ["gunicorn", "--config", "gunicorn-cfg.py", "run:app"]

Procfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: gunicorn run:app --log-file=-

apps/__init__.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# -*- encoding: utf-8 -*-
2+
"""
3+
Copyright (c) 2019 - present AppSeed.us
4+
"""
5+
6+
from flask import Flask
7+
from flask_login import LoginManager
8+
from flask_sqlalchemy import SQLAlchemy
9+
from importlib import import_module
10+
11+
12+
db = SQLAlchemy()
13+
login_manager = LoginManager()
14+
15+
16+
def register_extensions(app):
17+
db.init_app(app)
18+
login_manager.init_app(app)
19+
20+
21+
def register_blueprints(app):
22+
for module_name in ('authentication', 'home'):
23+
module = import_module('apps.{}.routes'.format(module_name))
24+
app.register_blueprint(module.blueprint)
25+
26+
27+
def configure_database(app):
28+
29+
@app.before_first_request
30+
def initialize_database():
31+
db.create_all()
32+
33+
@app.teardown_request
34+
def shutdown_session(exception=None):
35+
db.session.remove()
36+
37+
38+
def create_app(config):
39+
app = Flask(__name__)
40+
app.config.from_object(config)
41+
register_extensions(app)
42+
register_blueprints(app)
43+
configure_database(app)
44+
return app

apps/authentication/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# -*- encoding: utf-8 -*-
2+
"""
3+
Copyright (c) 2019 - present AppSeed.us
4+
"""
5+
6+
from flask import Blueprint
7+
8+
blueprint = Blueprint(
9+
'authentication_blueprint',
10+
__name__,
11+
url_prefix=''
12+
)

apps/authentication/forms.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- encoding: utf-8 -*-
2+
"""
3+
Copyright (c) 2019 - present AppSeed.us
4+
"""
5+
6+
from flask_wtf import FlaskForm
7+
from wtforms import TextField, PasswordField
8+
from wtforms.validators import Email, DataRequired
9+
10+
# login and registration
11+
12+
13+
class LoginForm(FlaskForm):
14+
username = TextField('Username',
15+
id='username_login',
16+
validators=[DataRequired()])
17+
password = PasswordField('Password',
18+
id='pwd_login',
19+
validators=[DataRequired()])
20+
21+
22+
class CreateAccountForm(FlaskForm):
23+
username = TextField('Username',
24+
id='username_create',
25+
validators=[DataRequired()])
26+
email = TextField('Email',
27+
id='email_create',
28+
validators=[DataRequired(), Email()])
29+
password = PasswordField('Password',
30+
id='pwd_create',
31+
validators=[DataRequired()])

apps/authentication/models.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# -*- encoding: utf-8 -*-
2+
"""
3+
Copyright (c) 2019 - present AppSeed.us
4+
"""
5+
6+
from flask_login import UserMixin
7+
8+
from apps import db, login_manager
9+
10+
from apps.authentication.util import hash_pass
11+
12+
class Users(db.Model, UserMixin):
13+
14+
__tablename__ = 'Users'
15+
16+
id = db.Column(db.Integer, primary_key=True)
17+
username = db.Column(db.String(64), unique=True)
18+
email = db.Column(db.String(64), unique=True)
19+
password = db.Column(db.LargeBinary)
20+
21+
def __init__(self, **kwargs):
22+
for property, value in kwargs.items():
23+
# depending on whether value is an iterable or not, we must
24+
# unpack it's value (when **kwargs is request.form, some values
25+
# will be a 1-element list)
26+
if hasattr(value, '__iter__') and not isinstance(value, str):
27+
# the ,= unpack of a singleton fails PEP8 (travis flake8 test)
28+
value = value[0]
29+
30+
if property == 'password':
31+
value = hash_pass(value) # we need bytes here (not plain str)
32+
33+
setattr(self, property, value)
34+
35+
def __repr__(self):
36+
return str(self.username)
37+
38+
39+
@login_manager.user_loader
40+
def user_loader(id):
41+
return Users.query.filter_by(id=id).first()
42+
43+
44+
@login_manager.request_loader
45+
def request_loader(request):
46+
username = request.form.get('username')
47+
user = Users.query.filter_by(username=username).first()
48+
return user if user else None

apps/authentication/routes.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# -*- encoding: utf-8 -*-
2+
"""
3+
Copyright (c) 2019 - present AppSeed.us
4+
"""
5+
6+
from flask import render_template, redirect, request, url_for
7+
from flask_login import (
8+
current_user,
9+
login_user,
10+
logout_user
11+
)
12+
13+
from apps import db, login_manager
14+
from apps.authentication import blueprint
15+
from apps.authentication.forms import LoginForm, CreateAccountForm
16+
from apps.authentication.models import Users
17+
18+
from apps.authentication.util import verify_pass
19+
20+
21+
@blueprint.route('/')
22+
def route_default():
23+
return redirect(url_for('authentication_blueprint.login'))
24+
25+
26+
# Login & Registration
27+
28+
@blueprint.route('/login', methods=['GET', 'POST'])
29+
def login():
30+
login_form = LoginForm(request.form)
31+
if 'login' in request.form:
32+
33+
# read form data
34+
username = request.form['username']
35+
password = request.form['password']
36+
37+
# Locate user
38+
user = Users.query.filter_by(username=username).first()
39+
40+
# Check the password
41+
if user and verify_pass(password, user.password):
42+
43+
login_user(user)
44+
return redirect(url_for('authentication_blueprint.route_default'))
45+
46+
# Something (user or pass) is not ok
47+
return render_template('accounts/login.html',
48+
msg='Wrong user or password',
49+
form=login_form)
50+
51+
if not current_user.is_authenticated:
52+
return render_template('accounts/login.html',
53+
form=login_form)
54+
return redirect(url_for('home_blueprint.index'))
55+
56+
57+
@blueprint.route('/register', methods=['GET', 'POST'])
58+
def register():
59+
create_account_form = CreateAccountForm(request.form)
60+
if 'register' in request.form:
61+
62+
username = request.form['username']
63+
email = request.form['email']
64+
65+
# Check usename exists
66+
user = Users.query.filter_by(username=username).first()
67+
if user:
68+
return render_template('accounts/register.html',
69+
msg='Username already registered',
70+
success=False,
71+
form=create_account_form)
72+
73+
# Check email exists
74+
user = Users.query.filter_by(email=email).first()
75+
if user:
76+
return render_template('accounts/register.html',
77+
msg='Email already registered',
78+
success=False,
79+
form=create_account_form)
80+
81+
# else we can create the user
82+
user = Users(**request.form)
83+
db.session.add(user)
84+
db.session.commit()
85+
86+
return render_template('accounts/register.html',
87+
msg='User created please <a href="/login">login</a>',
88+
success=True,
89+
form=create_account_form)
90+
91+
else:
92+
return render_template('accounts/register.html', form=create_account_form)
93+
94+
95+
@blueprint.route('/logout')
96+
def logout():
97+
logout_user()
98+
return redirect(url_for('authentication_blueprint.login'))
99+
100+
101+
# Errors
102+
103+
@login_manager.unauthorized_handler
104+
def unauthorized_handler():
105+
return render_template('home/page-403.html'), 403
106+
107+
108+
@blueprint.errorhandler(403)
109+
def access_forbidden(error):
110+
return render_template('home/page-403.html'), 403
111+
112+
113+
@blueprint.errorhandler(404)
114+
def not_found_error(error):
115+
return render_template('home/page-404.html'), 404
116+
117+
118+
@blueprint.errorhandler(500)
119+
def internal_error(error):
120+
return render_template('home/page-500.html'), 500

apps/authentication/util.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# -*- encoding: utf-8 -*-
2+
"""
3+
Copyright (c) 2019 - present AppSeed.us
4+
"""
5+
6+
import os
7+
import hashlib
8+
import binascii
9+
10+
# Inspiration -> https://www.vitoshacademy.com/hashing-passwords-in-python/
11+
12+
13+
def hash_pass(password):
14+
"""Hash a password for storing."""
15+
16+
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
17+
pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'),
18+
salt, 100000)
19+
pwdhash = binascii.hexlify(pwdhash)
20+
return (salt + pwdhash) # return bytes
21+
22+
23+
def verify_pass(provided_password, stored_password):
24+
"""Verify a stored password against one provided by user"""
25+
26+
stored_password = stored_password.decode('ascii')
27+
salt = stored_password[:64]
28+
stored_password = stored_password[64:]
29+
pwdhash = hashlib.pbkdf2_hmac('sha512',
30+
provided_password.encode('utf-8'),
31+
salt.encode('ascii'),
32+
100000)
33+
pwdhash = binascii.hexlify(pwdhash).decode('ascii')
34+
return pwdhash == stored_password

0 commit comments

Comments
 (0)