0

I'm refactoring my app.py for my flask application. I'm trying to get a user.py class handle all user related things. How should I structure my user.py class to return false if that user does not exist in my database?

app.py:

  db = get_db_connection() 
  user = User(db, request.form.get("email"))
  if not user.get_email(): # If user does not exist send user to registration page
     return redirect("/register", email=request.form.get("email")) # go to the registration page and fill in the used e-mail address
  
  # Check password and send to user_index if correct
  password = request.form.get('password')
  if user.check_password(password):
     session['userID'] = user.get_id()
     session['name'] = user.get_first_name()
     session['email'] = user.get_email()
     return redirect("/user_index")
  return message("Wrong Password. Go to log in page to try again.")

user.py:

class User:
    def __init__(self, database, email):
        db = database

        user = db.execute("SELECT * FROM users WHERE users.email = ?", [email]).fetchall()

        if user:
            self.__id = user['id']
            self.__last_name = user['lastName']
            self.__first_name = user['firstName']
            self.__email = user['email']
            self.__date_of_birth = user['dateOfBirth']
            self.__hash = user['hash']

I understand that when I instantiate an object in python it should return none. How can I structure my code so that if a user with the given email does not exist I could get some type of false value? Once I get a false value I should be able to redirect the users to the registration page.

If I'm doing this completely wrong please point me in the right direction.

1 Answer 1

1

There are many things to mention here:

  1. I recommend using an ORM like SQLAlchemy together with Flask. It will make your life a lot easier and you won't have to worry about some security issues (such as SQL Injection).
  2. If a user is going to log in, it is a good practice to return the message "wrong credentials" if their email OR password is incorrect, so an attacker will not know which one is correct or incorrect, therefore they will have many, many more combinations to try. That is, by just changing the phrase "wrong password" to "wrong credentials" you will be covering a security hole in your application. The same message should be returned if a user is not registered.
  3. I recommend reading up on the uses of underscore in Python, since you are apparently using it in a place where it isn't needed.
  4. In Python you don't need to use getters and setters unless you need to do some preprocessing before getting or setting an attribute.

Finally, in Flask this is a common way to structure a small project:

__init__.py

from flask import Flask

from utils import get_db_connection


app = Flask(__name__)
db = get_db_connection()

utils.py

# Do the necessary imports.


def get_db_connection():

    ...

run.py

from . import app


if __name__ == "__main__":
    app.run()  # You can set multiple settings in run().

routes.py

from . import app
from user import User


@app.route("/login", methods=["POST"])
def login():

    user = User.get(request.form.get("email"))

    # This is not recommended.
    # if not user:
    #     return redirect("/register", email=request.form.get("email"))

    password = request.form.get('password')

    if user and user.check_password(password):
        session['userID'] = user.id
        session['name'] = user.first_name
        session['email'] = user.email

        return redirect("/user_index")

    return message("Wrong credentials. Go to log in page to try again.")

models.py

from . import db


class User:

    def __init__(self, id, last_name, first_name, email, date_of_birth, hash):

        self.id = id
        self.last_name = last_name
        self.first_name = first_name
        self.email = email
        self.date_of_birth = date_of_birth
        self.hash = hash
    
    def check_password(self, password):

        return check_password_hash(self.hash, password)
    
    @staticmethod
    def get(email):

        user_result = db.execute("SELECT * FROM users WHERE users.email = ?", [email]).fetchall()

        if user_result:
            return User(user_result['id'], user_result['lastName'], user_result['firstName'], 
                        user_result['email'], user_result['dateOfBirth'], user_result['hash'])
        
        return None


# Declare more classes for your database models!
# class Admin:
#
#    ...
Sign up to request clarification or add additional context in comments.

9 Comments

A lot of good information. One question. How do I initialize the database in user.py? The way I have it setup now it the database gets passed as an argument to the user class. You have the database in the user class. I'm not too worried about security as this is a portfolio project for now. I'll move to something more secure if I choose to push this as an actually site I want people to use.
Also, good call on point number 2. I never thought about anything like that before.
I just edited the post to show a more appropriate way to structure a Flask project and avoid issues like "circular imports". Hope this answers your question about how to initialize the database.
Thank you so much for this thoughtful edit. I'll be going through it to learn what you have posted.
Thank you so much for all your help.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.