diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py index e29fd4ee12e9426b6278856cd262faa2df16c2d4..37061aa3e62a2c37c83fd73c5b957f3cdec8b6ec 100644 --- a/server/app/apis/auth.py +++ b/server/app/apis/auth.py @@ -3,7 +3,7 @@ All API calls concerning question answers. Default route: /api/auth """ -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone import app.core.http_codes as codes import app.database.controller as dbc @@ -114,22 +114,26 @@ class AuthLogin(Resource): if not item_user: api.abort(codes.UNAUTHORIZED, "Invalid email or password") + now = datetime.now() + # Login with existing email but with wrong password if not item_user.is_correct_password(password): # Increase the login attempts every time the user tries to login with wrong password item_user.login_attempts += 1 # Lock the user out for some time - if item_user.login_attempts == USER_LOGIN_LOCKED_ATTEMPTS: - item_user.locked = datetime.now() + USER_LOGIN_LOCKED_EXPIRES + if item_user.login_attempts >= USER_LOGIN_LOCKED_ATTEMPTS: + item_user.locked = now + USER_LOGIN_LOCKED_EXPIRES dbc.utils.commit() api.abort(codes.UNAUTHORIZED, "Invalid email or password") # Otherwise if login was successful but the user is locked if item_user.locked: + print(item_user.locked) + print(now) # Check if locked is greater than now - if item_user.locked > datetime.now(): + if item_user.locked.timestamp() > now.timestamp(): api.abort(codes.UNAUTHORIZED, f"Try again in {item_user.locked} hours.") else: item_user.locked = None diff --git a/server/app/database/controller/get.py b/server/app/database/controller/get.py index 2b85abed30e143c28e7ec7fe319eb08384236296..be2792a2d77c482911e3bae9bae6d4719dc1dcb3 100644 --- a/server/app/database/controller/get.py +++ b/server/app/database/controller/get.py @@ -129,7 +129,7 @@ def question(competition_id, slide_id, question_id): join_slide = Slide.id == Question.slide_id filters = (Competition.id == competition_id) & (Slide.id == slide_id) & (Question.id == question_id) - return Question.query.join(Competition, join_competition).join(Slide, join_slide).filter(filters).first_extended() + return Question.query.join(Slide, join_slide).join(Competition, join_competition).filter(filters).first_extended() def question_list(competition_id, slide_id): @@ -142,7 +142,7 @@ def question_list(competition_id, slide_id): join_slide = Slide.id == Question.slide_id filters = (Competition.id == competition_id) & (Slide.id == slide_id) - return Question.query.join(Competition, join_competition).join(Slide, join_slide).filter(filters).all() + return Question.query.join(Slide, join_slide).join(Competition, join_competition).filter(filters).all() def question_list_for_competition(competition_id): @@ -155,7 +155,7 @@ def question_list_for_competition(competition_id): join_slide = Slide.id == Question.slide_id filters = Competition.id == competition_id - return Question.query.join(Competition, join_competition).join(Slide, join_slide).filter(filters).all() + return Question.query.join(Slide, join_slide).join(Competition, join_competition).filter(filters).all() ### Question Alternative ### @@ -181,9 +181,9 @@ def question_alternative( ) return ( - QuestionAlternative.query.join(Competition, join_competition) + QuestionAlternative.query.join(Question, join_question) .join(Slide, join_slide) - .join(Question, join_question) + .join(Competition, join_competition) .filter(filters) .first_extended() ) @@ -201,9 +201,9 @@ def question_alternative_list(competition_id, slide_id, question_id): filters = (Competition.id == competition_id) & (Slide.id == slide_id) & (Question.id == question_id) return ( - QuestionAlternative.query.join(Competition, join_competition) + QuestionAlternative.query.join(Question, join_question) .join(Slide, join_slide) - .join(Question, join_question) + .join(Competition, join_competition) .filter(filters) .all() ) @@ -221,8 +221,8 @@ def question_score(competition_id, team_id, question_id, required=True): join_team = Team.id == QuestionScore.team_id filters = (Competition.id == competition_id) & (Team.id == team_id) & (QuestionScore.question_id == question_id) return ( - QuestionScore.query.join(Competition, join_competition) - .join(Team, join_team) + QuestionScore.query.join(Team, join_team) + .join(Competition, join_competition) .filter(filters) .first_extended(required) ) @@ -236,7 +236,7 @@ def question_score_list(competition_id, team_id): join_competition = Competition.id == Team.competition_id join_team = Team.id == QuestionScore.team_id filters = (Competition.id == competition_id) & (Team.id == team_id) - return QuestionScore.query.join(Competition, join_competition).join(Team, join_team).filter(filters).all() + return QuestionScore.query.join(Team, join_team).join(Competition, join_competition).filter(filters).all() def question_alternative_answer(competition_id, team_id, question_alternative_id, required=True): @@ -252,8 +252,8 @@ def question_alternative_answer(competition_id, team_id, question_alternative_id & (QuestionAlternativeAnswer.question_alternative_id == question_alternative_id) ) return ( - QuestionAlternativeAnswer.query.join(Competition, join_competition) - .join(Team, join_team) + QuestionAlternativeAnswer.query.join(Team, join_team) + .join(Competition, join_competition) .filter(filters) .first_extended(required) ) @@ -267,7 +267,7 @@ def question_alternative_answer_list(competition_id, team_id): join_competition = Competition.id == Team.competition_id join_team = Team.id == QuestionAlternativeAnswer.team_id filters = (Competition.id == competition_id) & (Team.id == team_id) - query = QuestionAlternativeAnswer.query.join(Competition, join_competition).join(Team, join_team).filter(filters) + query = QuestionAlternativeAnswer.query.join(Team, join_team).join(Competition, join_competition).filter(filters) # Get total score # sum = query.with_entities(func.sum(QuestionAnswer.score)).all() items = query.all() @@ -288,8 +288,8 @@ def component(competition_id, slide_id, component_id): poly = with_polymorphic(Component, [TextComponent, ImageComponent]) return ( db.session.query(poly) - .join(Competition, join_competition) .join(Slide, join_slide) + .join(Competition, join_competition) .filter(filters) .first_extended() ) @@ -304,7 +304,7 @@ def component_list(competition_id, slide_id): join_competition = Competition.id == Slide.competition_id join_slide = Slide.id == Component.slide_id filters = (Competition.id == competition_id) & (Slide.id == slide_id) - return Component.query.join(Competition, join_competition).join(Slide, join_slide).filter(filters).all() + return Component.query.join(Slide, join_slide).join(Competition, join_competition).filter(filters).all() ### Competitions ### diff --git a/server/app/database/models.py b/server/app/database/models.py index 45ae3797154b0cb706dd2716fe9bcac7928a46f3..bce3263ed1f7ca0e533d135c80d913da2dff246e 100644 --- a/server/app/database/models.py +++ b/server/app/database/models.py @@ -63,7 +63,7 @@ class User(db.Model): authenticated = db.Column(db.Boolean, default=False) login_attempts = db.Column(db.Integer, nullable=False, default=0) - locked = db.Column(db.DateTime(timezone=True), nullable=True, default=None) + locked = db.Column(db.DateTime(timezone=False), nullable=True, default=None) role_id = db.Column(db.Integer, db.ForeignKey("role.id"), nullable=False) city_id = db.Column(db.Integer, db.ForeignKey("city.id"), nullable=False) diff --git a/server/configmodule.py b/server/configmodule.py index 25978d378ee2e027730f91b7f025bb0080768e38..e5df0ea77fbc7a042054f76488ea3b7be0fe3243 100644 --- a/server/configmodule.py +++ b/server/configmodule.py @@ -2,51 +2,67 @@ import os from datetime import timedelta +class DevDbConfig: + HOST = "localhost" + PORT = 5432 + USER = "postgres" + PASSWORD = "password" + DATABASE = "teknik8" + SQLALCHEMY_DATABASE_URI = "postgresql://" + USER + ":" + PASSWORD + "@" + HOST + ":" + str(PORT) + "/" + DATABASE + + +class TestDbConfig: + HOST = "localhost" + PORT = 5432 + USER = "postgres" + PASSWORD = "password" + DATABASE = "teknik8-test" + SQLALCHEMY_DATABASE_URI = "postgresql://" + USER + ":" + PASSWORD + "@" + HOST + ":" + str(PORT) + "/" + DATABASE + + +class LiteDevDbConfig: + SQLALCHEMY_DATABASE_URI = "sqlite:///database.db" + + +class LiteTestDbConfig: + SQLALCHEMY_DATABASE_URI = "sqlite:///test.db" + + class Config: DEBUG = False TESTING = False BUNDLE_ERRORS = True - SQLALCHEMY_DATABASE_URI = "sqlite:///database.db" SQLALCHEMY_TRACK_MODIFICATIONS = False + SQLALCHEMY_ECHO = False JWT_SECRET_KEY = "super-secret" JWT_BLACKLIST_ENABLED = True JWT_BLACKLIST_TOKEN_CHECKS = ["access", "refresh"] JWT_ACCESS_TOKEN_EXPIRES = timedelta(days=2) # JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30) + JSON_SORT_KEYS = False UPLOADED_PHOTOS_DEST = os.path.join(os.getcwd(), "app", "static", "images") THUMBNAIL_SIZE = (120, 120) SECRET_KEY = os.urandom(24) - SQLALCHEMY_ECHO = False USER_LOGIN_LOCKED_ATTEMPTS = 12 USER_LOGIN_LOCKED_EXPIRES = timedelta(hours=3) - JSON_SORT_KEYS = False -class DevelopmentConfig(Config): +class DevelopmentConfig(Config, LiteDevDbConfig): DEBUG = True - SQLALCHEMY_ECHO = False - # HOST = "localhost" - # PORT = 5432 - # USER = "postgres" - # PASSWORD = "password" - # DATABASE = "teknik8" - # SQLALCHEMY_DATABASE_URI = "sqlite:///database.db" - # SQLALCHEMY_DATABASE_URI = "postgresql://" + USER + ":" + PASSWORD + "@" + HOST + ":" + str(PORT) + "/" + DATABASE -class TestingConfig(Config): +class TestingConfig(Config, LiteTestDbConfig): TESTING = True - SQLALCHEMY_DATABASE_URI = "sqlite:///test.db" USER_LOGIN_LOCKED_ATTEMPTS = 4 USER_LOGIN_LOCKED_EXPIRES = timedelta(seconds=4) class ProductionConfig(Config): - SQLALCHEMY_DATABASE_URI = "sqlite:///database.db" + DEBUG = False + TESTING = False # HOST = "localhost" # PORT = 5432 # USER = "postgres" # PASSWORD = "password" # DATABASE = "teknik8" - # SQLALCHEMY_DATABASE_URI = "sqlite:///database.db" # SQLALCHEMY_DATABASE_URI = "postgresql://" + USER + ":" + PASSWORD + "@" + HOST + ":" + str(PORT) + "/" + DATABASE diff --git a/server/populate.py b/server/populate.py index 935acce66c2d2c8a083e90f040ba2d778bb61c29..43557ba9a4858807dc61dc7c64d54723542c9dfe 100644 --- a/server/populate.py +++ b/server/populate.py @@ -84,16 +84,16 @@ def _add_items(): for k in range(3): x = random.randrange(1, 500) y = random.randrange(1, 500) - w = random.randrange(150, 400) - h = random.randrange(150, 400) + w = 350 + h = 50 dbc.add.component( 1, item_slide.id, 1, x, y, w, h, text=f"<p><span style='font-size: 24pt;'>{k}</span></p>" ) for k in range(3): x = random.randrange(1, 500) y = random.randrange(1, 500) - w = random.randrange(150, 400) - h = random.randrange(150, 400) + w = 350 + h = 50 dbc.add.component( 1, item_slide.id, 3, x, y, w, h, text=f"<p><span style='font-size: 24pt;'>{k}</span></p>" )