diff --git a/api/conf/auth.py b/api/conf/auth.py index bc8a548..017b88b 100644 --- a/api/conf/auth.py +++ b/api/conf/auth.py @@ -1,14 +1,26 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +import os + from flask_httpauth import HTTPTokenAuth from itsdangerous import TimedJSONWebSignatureSerializer as JsonWebToken +# Get JWT secret from environment. +JWT_SECRET = os.environ.get("JWT_SECRET") +if not JWT_SECRET: + raise RuntimeError("JWT_SECRET environment variable must be set") + +# Get refresh JWT secret from environment. +REFRESH_JWT_SECRET = os.environ.get("REFRESH_JWT_SECRET") +if not REFRESH_JWT_SECRET: + raise RuntimeError("REFRESH_JWT_SECRET environment variable must be set") + # JWT creation. -jwt = JsonWebToken("top secret!", expires_in=3600) +jwt = JsonWebToken(JWT_SECRET, expires_in=3600) # Refresh token creation. -refresh_jwt = JsonWebToken("telelelele", expires_in=7200) +refresh_jwt = JsonWebToken(REFRESH_JWT_SECRET, expires_in=7200) # Auth object creation. auth = HTTPTokenAuth("Bearer") diff --git a/api/db_initializer/db_initializer.py b/api/db_initializer/db_initializer.py index 0602df2..99b5f5a 100644 --- a/api/db_initializer/db_initializer.py +++ b/api/db_initializer/db_initializer.py @@ -18,10 +18,10 @@ def create_super_admin(): # Create admin user if it does not existed. user = User( username="sa_username", - password="sa_password", email="sa_email@example.com", user_role="sa", ) + user.set_password("sa_password") # Add user to session. db.session.add(user) @@ -49,10 +49,10 @@ def create_admin_user(): # Create admin user if it does not existed. user = User( username="admin_username", - password="admin_password", email="admin_email@example.com", user_role="admin", ) + user.set_password("admin_password") # Add user to session. db.session.add(user) @@ -82,13 +82,12 @@ def create_test_user( if user is None: # Create admin user if it does not existed. - # user = User(username=username, password=password, email=email, user_role=user_role) user = User( username=username, - password=password, email=email, user_role=user_role, ) + user.set_password(password) # Add user to session. db.session.add(user) diff --git a/api/handlers/UserHandlers.py b/api/handlers/UserHandlers.py index 9d88fef..b1e5248 100644 --- a/api/handlers/UserHandlers.py +++ b/api/handlers/UserHandlers.py @@ -52,7 +52,8 @@ def post(): return error.ALREADY_EXIST # Create a new user. - user = User(username=username, password=password, email=email) + user = User(username=username, email=email) + user.set_password(password) # Add user to session. db.session.add(user) @@ -88,10 +89,10 @@ def post(): return error.INVALID_INPUT_422 # Get user if it is existed. - user = User.query.filter_by(email=email, password=password).first() + user = User.query.filter_by(email=email).first() - # Check if user is not existed. - if user is None: + # Check if user is not existed or password does not match. + if user is None or not user.check_password(password): return error.UNAUTHORIZED if user.user_role == "user": @@ -200,13 +201,13 @@ def post(self): user = User.query.filter_by(email=g.user).first() # Check if user password does not match with old password. - if user.password != old_pass: + if not user.check_password(old_pass): # Return does not match status. return {"status": "old password does not match."} # Update password. - user.password = new_pass + user.set_password(new_pass) # Commit session. db.session.commit() diff --git a/api/models/models.py b/api/models/models.py index 7f4543d..b700725 100644 --- a/api/models/models.py +++ b/api/models/models.py @@ -3,6 +3,7 @@ from datetime import datetime +import bcrypt from flask import g from api.conf.auth import auth, jwt @@ -32,6 +33,18 @@ class User(db.Model): # Unless otherwise stated default role is user. user_role = db.Column(db.String, default="user") + def set_password(self, password): + """Hash and set the user password.""" + self.password = bcrypt.hashpw( + password.encode("utf-8"), bcrypt.gensalt() + ).decode("utf-8") + + def check_password(self, password): + """Check hashed password.""" + return bcrypt.checkpw( + password.encode("utf-8"), self.password.encode("utf-8") + ) + # Generates auth token. def generate_auth_token(self, permission_level): @@ -90,10 +103,9 @@ def verify_auth_token(token): def __repr__(self): # This is only for representation how you want to see user information after query. - return "" % ( + return "" % ( self.id, self.username, - self.password, self.email, self.created, ) diff --git a/main.py b/main.py index 344bbbd..328122b 100644 --- a/main.py +++ b/main.py @@ -8,9 +8,6 @@ from api.conf.config import SQLALCHEMY_DATABASE_URI from api.conf.routes import generate_routes from api.database.database import db -from api.db_initializer.db_initializer import (create_admin_user, - create_super_admin, - create_test_user) def create_app(): @@ -18,8 +15,17 @@ def create_app(): # Create a flask app. app = Flask(__name__) - # Set debug true for catching the errors. - app.config['DEBUG'] = True + # Set secret key from environment. + SECRET_KEY = os.environ.get("SECRET_KEY") + if not SECRET_KEY: + raise RuntimeError( + "SECRET_KEY environment variable must be set. " + "Generate one with: python -c 'import secrets; print(secrets.token_hex(32))'" + ) + app.config["SECRET_KEY"] = SECRET_KEY + + # Set debug from environment (default: False for production safety). + app.config['DEBUG'] = os.environ.get("DEBUG", "False").lower() in ("true", "1", "yes") # Set database url. app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI @@ -41,15 +47,6 @@ def create_app(): # Create all database tables. db.create_all() - # Create default super admin user in database. - create_super_admin() - - # Create default admin user in database. - create_admin_user() - - # Create default test user in database. - create_test_user() - # Return app. return app diff --git a/requirements.txt b/requirements.txt index 0035bc0..60e1d72 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +bcrypt==4.0.1 aniso8601==9.0.1 click==7.1.2 Flask==1.1.2