From 75f244ae9f2baad88d680d1950e7bc1c29437576 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Fri, 4 Oct 2019 23:25:01 -0700 Subject: [PATCH 1/4] add auth routes --- auth/__init__.py | 0 auth/auth.py | 14 ++++++++++++++ models/User.py | 35 +++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 4 files changed, 50 insertions(+) create mode 100644 auth/__init__.py create mode 100644 auth/auth.py diff --git a/auth/__init__.py b/auth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth/auth.py b/auth/auth.py new file mode 100644 index 0000000..ffee7a5 --- /dev/null +++ b/auth/auth.py @@ -0,0 +1,14 @@ +from flask import Blueprint, request, jsonify, session +from ..models.User import User + +auth = Blueprint('auth', __name__, url_prefix='/auth') + +@api.route('/signup', methods=['POST']) +def auth_signup(): + response = {"message": "signup post"} + return jsonify(response) + +@api.route('/login', methods=['POST']) +def auth_login(): + response = {"message": "login post"} + return jsonify(response) \ No newline at end of file diff --git a/models/User.py b/models/User.py index 83a9302..4ae7c17 100644 --- a/models/User.py +++ b/models/User.py @@ -1,6 +1,7 @@ from app import db, ma, bcrypt import datetime import enum +import jwt class Ranks(enum.Enum): # with minimal Elo rating D7 = "Seven Dan" # Elo 2700+ @@ -61,3 +62,37 @@ class User(db.Model): ).decode() self.registered_on = datetime.datetime.now() self.admin = admin + + def encode_auth_token(self, user_id): + """ + Generates the Auth Token + :return: string + """ + try: + payload = { + 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=0, seconds=5), + 'iat': datetime.datetime.utcnow(), + 'sub': user_id + } + return jwt.encode( + payload, + app.config.get('SECRET_KEY'), + algorithm='HS256' + ) + except Exception as e: + return e + + @staticmethod + def decode_auth_token(auth_token): + """ + Decodes the auth token + :param auth_token: + :return: integer|string + """ + try: + payload = jwt.decode(auth_token, app.config.get('SECRET_KEY')) + return payload['sub'] + except jwt.ExpiredSignatureError: + return 'Signature expired. Please log in again.' + except jwt.InvalidTokenError: + return 'Invalid token. Please log in again.' diff --git a/requirements.txt b/requirements.txt index f300d47..fab2ace 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,6 +23,7 @@ mccabe==0.6.1 numpy==1.17.2 psycopg2==2.8.3 pycparser==2.19 +PyJWT==1.7.1 pylint==2.4.2 python-dateutil==2.8.0 python-editor==1.0.4 From c8d56b5e1e46af34b1f16402e45c042395821d4c Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Fri, 4 Oct 2019 23:29:43 -0700 Subject: [PATCH 2/4] register auth blueprint --- app.py | 3 ++- auth/auth.py | 6 +++--- models/User.py | 34 +++++++++++++++++----------------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/app.py b/app.py index a689d04..10fb0ea 100644 --- a/app.py +++ b/app.py @@ -49,9 +49,10 @@ def hello_world(): # Blue prints from api.api import api +from auth.auth import auth app.register_blueprint(api) - +app.register_blueprint(auth) if __name__ == '__main__': app.run(debug=DEBUG, port=PORT) \ No newline at end of file diff --git a/auth/auth.py b/auth/auth.py index ffee7a5..94e1723 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -1,14 +1,14 @@ from flask import Blueprint, request, jsonify, session -from ..models.User import User +import models auth = Blueprint('auth', __name__, url_prefix='/auth') -@api.route('/signup', methods=['POST']) +@auth.route('/signup', methods=['POST']) def auth_signup(): response = {"message": "signup post"} return jsonify(response) -@api.route('/login', methods=['POST']) +@auth.route('/login', methods=['POST']) def auth_login(): response = {"message": "login post"} return jsonify(response) \ No newline at end of file diff --git a/models/User.py b/models/User.py index 4ae7c17..b71c9c6 100644 --- a/models/User.py +++ b/models/User.py @@ -64,23 +64,23 @@ class User(db.Model): self.admin = admin def encode_auth_token(self, user_id): - """ - Generates the Auth Token - :return: string - """ - try: - payload = { - 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=0, seconds=5), - 'iat': datetime.datetime.utcnow(), - 'sub': user_id - } - return jwt.encode( - payload, - app.config.get('SECRET_KEY'), - algorithm='HS256' - ) - except Exception as e: - return e + """ + Generates the Auth Token + :return: string + """ + try: + payload = { + 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=0, seconds=5), + 'iat': datetime.datetime.utcnow(), + 'sub': user_id + } + return jwt.encode( + payload, + app.config.get('SECRET_KEY'), + algorithm='HS256' + ) + except Exception as e: + return e @staticmethod def decode_auth_token(auth_token): From 7c14f451bb5dc686a8e2320d4c958e5ed1622018 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sat, 5 Oct 2019 17:49:49 -0700 Subject: [PATCH 3/4] create app factory pattern --- api/api.py | 1 - app.py | 58 ------------------------------- auth/auth.py | 4 +-- __init__.py => config/__init__.py | 0 config.py => config/config.py | 1 + config/models_mount.py | 7 ++++ database.py | 15 ++++++++ models.py | 6 ---- models/User.py | 2 +- server.py | 36 +++++++++++++++++++ 10 files changed, 62 insertions(+), 68 deletions(-) delete mode 100644 app.py rename __init__.py => config/__init__.py (100%) rename config.py => config/config.py (97%) create mode 100644 config/models_mount.py create mode 100644 database.py delete mode 100644 models.py create mode 100644 server.py diff --git a/api/api.py b/api/api.py index 5b1e268..e27790a 100644 --- a/api/api.py +++ b/api/api.py @@ -3,7 +3,6 @@ from .users.user_endpoint import UserEndpoint api = Blueprint('api', __name__, url_prefix='/api') - @api.route('/home', methods=['GET']) def api_home(): response = {"message": "home page"} diff --git a/app.py b/app.py deleted file mode 100644 index 10fb0ea..0000000 --- a/app.py +++ /dev/null @@ -1,58 +0,0 @@ -import os - -from flask import Flask - -# ! SQLAlchemy > Marshmallow - these must be imported in this order -from flask_sqlalchemy import SQLAlchemy -from flask_marshmallow import Marshmallow - -from flask_migrate import Migrate - -from flask_bcrypt import Bcrypt -from flask_cors import CORS - -app = Flask(__name__) -CORS(app) - -# config database -app_settings = os.getenv( - 'APP_SETTINGS', - 'config.DevelopmentConfig' -) - -app.config.from_object(app_settings) - -# init bcrypt -bcrypt = Bcrypt(app) - -# init database -db = SQLAlchemy(app) - -# init marshmallow -ma = Marshmallow(app) - -# init all db models -import models - -migrate = Migrate(app, db) - - -# dev server -DEBUG = True -PORT = 8000 - -# Routes - -@app.route('/') -def hello_world(): - return 'Hello World' - -# Blue prints -from api.api import api -from auth.auth import auth - -app.register_blueprint(api) -app.register_blueprint(auth) - -if __name__ == '__main__': - app.run(debug=DEBUG, port=PORT) \ No newline at end of file diff --git a/auth/auth.py b/auth/auth.py index 94e1723..c1de946 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -1,6 +1,6 @@ from flask import Blueprint, request, jsonify, session -import models - +from models import User +print(User) auth = Blueprint('auth', __name__, url_prefix='/auth') @auth.route('/signup', methods=['POST']) diff --git a/__init__.py b/config/__init__.py similarity index 100% rename from __init__.py rename to config/__init__.py diff --git a/config.py b/config/config.py similarity index 97% rename from config.py rename to config/config.py index 0b3f00f..55c5173 100644 --- a/config.py +++ b/config/config.py @@ -14,6 +14,7 @@ class DevelopmentConfig(BaseConfig): DEBUG = True BCRYPT_LOG_ROUNDS = 4 SQLALCHEMY_DATABASE_URI = DATABASE + PORT = 8000 class TestingConfig(BaseConfig): diff --git a/config/models_mount.py b/config/models_mount.py new file mode 100644 index 0000000..fa36978 --- /dev/null +++ b/config/models_mount.py @@ -0,0 +1,7 @@ +if __name__ == '__main__': + from ..models.User import User + from ..models.GameRoom import GameRoom + from ..models.TimeSettings import TimeSettings + from ..models.Game import Game + from ..models.Move import Move + from ..models.Message import Message \ No newline at end of file diff --git a/database.py b/database.py new file mode 100644 index 0000000..c43b153 --- /dev/null +++ b/database.py @@ -0,0 +1,15 @@ + +# ! SQLAlchemy > Marshmallow - these must be imported in this order +from flask_sqlalchemy import SQLAlchemy +from flask_marshmallow import Marshmallow +from flask_bcrypt import Bcrypt + +# init bcrypt +def bcrypt(app): + Bcrypt(app) + +# init database +db = SQLAlchemy() + +# init marshmallow +ma = Marshmallow() diff --git a/models.py b/models.py deleted file mode 100644 index 5fa00f1..0000000 --- a/models.py +++ /dev/null @@ -1,6 +0,0 @@ -from models.User import User -from models.GameRoom import GameRoom -from models.TimeSettings import TimeSettings -from models.Game import Game -from models.Move import Move -from models.Message import Message \ No newline at end of file diff --git a/models/User.py b/models/User.py index b71c9c6..a22ff84 100644 --- a/models/User.py +++ b/models/User.py @@ -1,4 +1,4 @@ -from app import db, ma, bcrypt +from database import db, ma, bcrypt import datetime import enum import jwt diff --git a/server.py b/server.py new file mode 100644 index 0000000..c82ac0d --- /dev/null +++ b/server.py @@ -0,0 +1,36 @@ +import os +from database import db, ma, bcrypt + +from flask import Flask + +from flask_migrate import Migrate + +from flask_cors import CORS + +# Blueprints +from api.api import api +from auth.auth import auth + +import config.models_mount + +def create_app(): + app = Flask(__name__) + bcrypt(app) + CORS(app) + app_settings = os.getenv( + 'APP_SETTINGS', + 'config.config.DevelopmentConfig' + ) + + app.config.from_object(app_settings) + db.init_app(app) + ma.init_app(app) + app.register_blueprint(api) + app.register_blueprint(auth) + migrate = Migrate(app, db) + return app + + +if __name__ == '__main__': + app = create_app() + app.run(port=8000, debug=True) \ No newline at end of file From cc3b9eebf5044866ba27ef0c60dc217368789c47 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sat, 5 Oct 2019 22:40:02 -0700 Subject: [PATCH 4/4] debug server problems, implement auth route --- config/__init__.py => __init__.py | 0 api/users/user_endpoint.py | 4 ++- app.py | 20 +++++++++++ auth/auth.py | 43 ++++++++++++++++++++--- configuration/__init__.py | 0 {config => configuration}/config.py | 0 {config => configuration}/models_mount.py | 0 database.py | 5 --- manage.py | 11 +++++- migrations/versions/50aab465cf44_.py | 28 +++++++++++++++ migrations/versions/9519d28f13f0_.py | 2 +- migrations/versions/bdc3cd5d7499_.py | 28 +++++++++++++++ migrations/versions/bfa4b406e22f_.py | 28 +++++++++++++++ models/User.py | 25 +++++++++++-- server.py | 31 ++++------------ 15 files changed, 185 insertions(+), 40 deletions(-) rename config/__init__.py => __init__.py (100%) create mode 100644 app.py create mode 100644 configuration/__init__.py rename {config => configuration}/config.py (100%) rename {config => configuration}/models_mount.py (100%) create mode 100644 migrations/versions/50aab465cf44_.py create mode 100644 migrations/versions/bdc3cd5d7499_.py create mode 100644 migrations/versions/bfa4b406e22f_.py diff --git a/config/__init__.py b/__init__.py similarity index 100% rename from config/__init__.py rename to __init__.py diff --git a/api/users/user_endpoint.py b/api/users/user_endpoint.py index e147fed..7b89b6f 100644 --- a/api/users/user_endpoint.py +++ b/api/users/user_endpoint.py @@ -1,5 +1,7 @@ +from models.User import User, user_schema class UserEndpoint(object): def users(): - response = {"message": "users page"} + user = User.query.first() + response = user_schema.dumps(user) return response diff --git a/app.py b/app.py new file mode 100644 index 0000000..e1b6270 --- /dev/null +++ b/app.py @@ -0,0 +1,20 @@ +import os +from database import db, ma + +from flask import Flask + +from configuration.config import DevelopmentConfig + +from flask_bcrypt import Bcrypt +from flask_cors import CORS + +def create_app(): + app = Flask(__name__) + CORS(app) + + app.config.from_object(DevelopmentConfig) + db.init_app(app) + ma.init_app(app) + return app + +bcrypt = Bcrypt(create_app()) \ No newline at end of file diff --git a/auth/auth.py b/auth/auth.py index c1de946..b199d24 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -1,12 +1,47 @@ from flask import Blueprint, request, jsonify, session -from models import User -print(User) + +from database import db +from models.User import User + auth = Blueprint('auth', __name__, url_prefix='/auth') @auth.route('/signup', methods=['POST']) def auth_signup(): - response = {"message": "signup post"} - return jsonify(response) + data = request.get_json() + user = User.query.filter_by(email=data.get('email')).first() + if not user: + try: + print('getting here 1') + user = User( + email = data['email'], + password = data['password'], + ) + print('getting here 2') + db.session.add(user) + print('wtf') + db.session.commit() + print('user') + auth_token = user.encode_auth_token(user.id) + print('getting here 4') + response = { + 'status': 'success', + 'message': 'Succesfully registered.', + 'auth_token': auth_token.decode() + } + return jsonify(response), 201 + except Exception as e: + print(e.__dict__) + response = { + 'status': 'fail', + 'message': 'There was an error. Please try again.' + } + return jsonify(response), 401 + else: + response = { + 'status': 'fail', + 'message': 'User already exists. Please login.' + } + return jsonify(response), 202 @auth.route('/login', methods=['POST']) def auth_login(): diff --git a/configuration/__init__.py b/configuration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/config.py b/configuration/config.py similarity index 100% rename from config/config.py rename to configuration/config.py diff --git a/config/models_mount.py b/configuration/models_mount.py similarity index 100% rename from config/models_mount.py rename to configuration/models_mount.py diff --git a/database.py b/database.py index c43b153..093d45e 100644 --- a/database.py +++ b/database.py @@ -2,12 +2,7 @@ # ! SQLAlchemy > Marshmallow - these must be imported in this order from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow -from flask_bcrypt import Bcrypt -# init bcrypt -def bcrypt(app): - Bcrypt(app) - # init database db = SQLAlchemy() diff --git a/manage.py b/manage.py index 70172ba..35d0d9d 100644 --- a/manage.py +++ b/manage.py @@ -4,11 +4,20 @@ import unittest from flask_script import Manager from flask_migrate import Migrate, MigrateCommand -from app import app, db +from database import db +from app import create_app +app = create_app() migrate = Migrate(app, db) manager = Manager(app) +from models.Game import Game +from models.GameRoom import GameRoom +from models.Message import Message +from models.Move import Move +from models.TimeSettings import TimeSettings +from models.User import User + # migrations manager.add_command('db', MigrateCommand) diff --git a/migrations/versions/50aab465cf44_.py b/migrations/versions/50aab465cf44_.py new file mode 100644 index 0000000..6aade5b --- /dev/null +++ b/migrations/versions/50aab465cf44_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: 50aab465cf44 +Revises: bfa4b406e22f +Create Date: 2019-10-05 22:37:07.643522 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '50aab465cf44' +down_revision = 'bfa4b406e22f' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('users', sa.Column('password', sa.String(length=255), nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('users', 'password') + # ### end Alembic commands ### diff --git a/migrations/versions/9519d28f13f0_.py b/migrations/versions/9519d28f13f0_.py index 1f327ae..8633673 100644 --- a/migrations/versions/9519d28f13f0_.py +++ b/migrations/versions/9519d28f13f0_.py @@ -39,7 +39,7 @@ def upgrade(): op.create_table('users', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('email', sa.String(length=255), nullable=False), - sa.Column('password', sa.DateTime(), nullable=False), + sa.Column('password', sa.Integer(length=255), nullable=False), sa.Column('registered_on', sa.DateTime(), nullable=False), sa.Column('admin', sa.Boolean(), nullable=False), sa.Column('rank', sa.Enum('D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'K1', 'K2', 'K3', 'K4', 'K5', 'K6', 'K7', 'K8', 'K9', 'K10', 'K11', 'K12', 'K13', 'K14', 'K15', 'K16', 'K17', 'K18', 'K19', 'K20', 'K21', 'K22', 'K23', 'K24', 'K25', 'K26', 'K27', 'K28', 'K29', 'K30', name='ranks'), nullable=True), diff --git a/migrations/versions/bdc3cd5d7499_.py b/migrations/versions/bdc3cd5d7499_.py new file mode 100644 index 0000000..d094cdc --- /dev/null +++ b/migrations/versions/bdc3cd5d7499_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: bdc3cd5d7499 +Revises: 16c70b11242e +Create Date: 2019-10-05 22:34:04.358841 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'bdc3cd5d7499' +down_revision = '16c70b11242e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('moves', sa.Column('is_pass', sa.Boolean(), nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('moves', 'is_pass') + # ### end Alembic commands ### diff --git a/migrations/versions/bfa4b406e22f_.py b/migrations/versions/bfa4b406e22f_.py new file mode 100644 index 0000000..525da5f --- /dev/null +++ b/migrations/versions/bfa4b406e22f_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: bfa4b406e22f +Revises: bdc3cd5d7499 +Create Date: 2019-10-05 22:36:45.420054 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'bfa4b406e22f' +down_revision = 'bdc3cd5d7499' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('users', 'password') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('users', sa.Column('password', postgresql.TIMESTAMP(), autoincrement=False, nullable=False)) + # ### end Alembic commands ### diff --git a/models/User.py b/models/User.py index a22ff84..203c25a 100644 --- a/models/User.py +++ b/models/User.py @@ -1,4 +1,6 @@ -from database import db, ma, bcrypt +from database import db, ma +from app import bcrypt +from configuration import config import datetime import enum import jwt @@ -48,7 +50,7 @@ class User(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) email = db.Column(db.String(255), unique=True, nullable=False) - password = db.Column(db.DateTime, nullable=False) + password = db.Column(db.String(255), nullable=False) registered_on = db.Column(db.DateTime, nullable=False) admin = db.Column(db.Boolean, nullable=False, default=False) rank = db.Column(db.Enum(Ranks)) @@ -56,10 +58,13 @@ class User(db.Model): rank_certainty = db.Column(db.Boolean, nullable=False, default=False) def __init__(self, email, password, admin=False,): + print('user init') self.email = email + print('user email init') self.password = bcrypt.generate_password_hash( - password, app.config.get('BCRYPT_LOG_ROUNDS') + password, 13 ).decode() + print('user password init') self.registered_on = datetime.datetime.now() self.admin = admin @@ -96,3 +101,17 @@ class User(db.Model): return 'Signature expired. Please log in again.' except jwt.InvalidTokenError: return 'Invalid token. Please log in again.' + +class UserSchema(ma.ModelSchema): + class Meta: + fields = ( + 'id', + 'name', + 'registered_on', + 'rank', + 'rank_certainty', + 'elo' + ) + +user_schema = UserSchema() +users_schema = UserSchema(many=True) \ No newline at end of file diff --git a/server.py b/server.py index c82ac0d..d38827c 100644 --- a/server.py +++ b/server.py @@ -1,36 +1,17 @@ -import os -from database import db, ma, bcrypt - -from flask import Flask - -from flask_migrate import Migrate - -from flask_cors import CORS +from app import create_app, db # Blueprints from api.api import api from auth.auth import auth -import config.models_mount +import configuration.models_mount +from flask_migrate import Migrate -def create_app(): - app = Flask(__name__) - bcrypt(app) - CORS(app) - app_settings = os.getenv( - 'APP_SETTINGS', - 'config.config.DevelopmentConfig' - ) - - app.config.from_object(app_settings) - db.init_app(app) - ma.init_app(app) - app.register_blueprint(api) - app.register_blueprint(auth) - migrate = Migrate(app, db) - return app if __name__ == '__main__': app = create_app() + app.register_blueprint(api) + app.register_blueprint(auth) + migrate = Migrate(app, db) app.run(port=8000, debug=True) \ No newline at end of file