diff options
Diffstat (limited to 'api/auth')
| -rw-r--r-- | api/auth/login.py | 80 | ||||
| -rw-r--r-- | api/auth/login_token.py | 20 | ||||
| -rw-r--r-- | api/auth/signup.py | 126 | ||||
| -rw-r--r-- | api/auth/token.py | 66 | 
4 files changed, 146 insertions, 146 deletions
diff --git a/api/auth/login.py b/api/auth/login.py index 1d5a4b2..e3d5fde 100644 --- a/api/auth/login.py +++ b/api/auth/login.py @@ -8,46 +8,46 @@ login = Blueprint('login', __name__)  @login.route('/login', methods=['POST'])  def index(): -	data = request.get_json() - -	# get form data -	email = data.get("email") or "" -	password = data.get("password") or "" - -	# return malformed request if email or password is missing -	if not email or not password: -		return "", 400 - -	# resolve user_id from username or email -	user_id = None -	user_id = user_id or cursor.execute( -		"select user_id from users where email = ?", [email] -	).fetchone() -	user_id = user_id or cursor.execute( -		"select user_id from users where lower(username) = lower(?)", [email] -	).fetchone() -	if user_id == None: return "", 401 - -	# check the password -	passwd = cursor.execute( -		"select password_hash from users where user_id = ?", [user_id[0]] -	).fetchone() -	check = passwords.check_password(password, passwd[0]) -	if not check: return "", 401 - -	# generate a new authentication token and add it to the users valid token list -	new_token = token.generate_token() -	token.add_token(user_id[0], token.hash_token(new_token)) - -	# make response with the set_cookie header -	res = make_response("", 200) -	res.set_cookie( -		"token", -		new_token["token"], -		expires=int(new_token["expirationDate"] / 1000) -	) - -	return res +    data = request.get_json() + +    # get form data +    email = data.get("email") or "" +    password = data.get("password") or "" + +    # return malformed request if email or password is missing +    if not email or not password: +        return "", 400 + +    # resolve user_id from username or email +    user_id = None +    user_id = user_id or cursor.execute( +        "select user_id from users where email = ?", [email] +    ).fetchone() +    user_id = user_id or cursor.execute( +        "select user_id from users where lower(username) = lower(?)", [email] +    ).fetchone() +    if user_id == None: return "", 401 + +    # check the password +    passwd = cursor.execute( +        "select password_hash from users where user_id = ?", [user_id[0]] +    ).fetchone() +    check = passwords.check_password(password, passwd[0]) +    if not check: return "", 401 + +    # generate a new authentication token and add it to the users valid token list +    new_token = token.generate_token() +    token.add_token(user_id[0], token.hash_token(new_token)) + +    # make response with the set_cookie header +    res = make_response("", 200) +    res.set_cookie( +        "token", +        new_token["token"], +        expires=int(new_token["expirationDate"] / 1000) +    ) + +    return res  dynamic_route = ["/auth", login] diff --git a/api/auth/login_token.py b/api/auth/login_token.py index b5b1579..de770b3 100644 --- a/api/auth/login_token.py +++ b/api/auth/login_token.py @@ -5,12 +5,12 @@ from auth.token import validate_token, hash_token  # get user_id from authentication token  def token_login(token): -	hashed = hash_token({"token": token, "expirationDate": 0}) -	user_id = cursor.execute( -		"select user_id from users where valid_tokens like ?", -		[f"%{hashed['token']}%"] -	).fetchone() -	return None if not user_id else user_id[0] +    hashed = hash_token({"token": token, "expirationDate": 0}) +    user_id = cursor.execute( +        "select user_id from users where valid_tokens like ?", +        [f"%{hashed['token']}%"] +    ).fetchone() +    return None if not user_id else user_id[0]  token = Blueprint('token', __name__) @@ -19,12 +19,12 @@ token = Blueprint('token', __name__)  # this endpoint is currently unused, but verifies that a token is valid  @token.route('/token', methods=['POST'])  def index(): -	data = request.get_json() +    data = request.get_json() -	auth_token = data.get("token") or "" -	if not auth_token: return "", 400 +    auth_token = data.get("token") or "" +    if not auth_token: return "", 400 -	return "", 200 if token_login(auth_token) else 401 +    return "", 200 if token_login(auth_token) else 401  dynamic_route = ["/auth", token] diff --git a/api/auth/signup.py b/api/auth/signup.py index 98c07a7..a18796b 100644 --- a/api/auth/signup.py +++ b/api/auth/signup.py @@ -9,20 +9,20 @@ import re  # checks if the usename is between 3 and 35 charachters  def validate_username(username): -	return len(username) in range(3, 35 + 1) +    return len(username) in range(3, 35 + 1)  # garbage email validation (see todo)  def validate_email(email): -	#TODO: use node_modules/email-validator/index.js -	return len(email) > 1 and \ -                                      "@" in email +    #TODO: use node_modules/email-validator/index.js +    return len(email) > 1 and \ +                                         "@" in email  # checks if the password is safe (regex explanation in pages/register.tsx)  def validate_password(password): -	passwordRegex = r"^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$"  # r"" = raw string -	return re.match(passwordRegex, password) +    passwordRegex = r"^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$"  # r"" = raw string +    return re.match(passwordRegex, password)  signup = Blueprint('signup', __name__) @@ -30,63 +30,63 @@ signup = Blueprint('signup', __name__)  @signup.route('/signup', methods=['POST'])  def index(): -	# parse request data if the content-type header is set to application/json -	data = request.get_json() - -	# force string if {}.get(...) returns None -	username = data.get("username") or "" -	email = data.get("email") or "" -	password = data.get("password") or "" - -	# return 400 (malformed request) if any of the required data is missing -	if not username or \ -                                     not email or \ -                                     not password: -		return "", 400 - -	# return 403 (forbidden) if any of the required data is invalid -	if not validate_username(username) or \ -                                     not validate_email(email) or \ -                                     not validate_password(password): -		return {"error": "form_data_invalid"}, 403 - -	# check if username is taken -	if cursor.execute( -		"select username from users where lower(username) = lower(?)", -		[username] -	).fetchone(): -		return {"error": "username_taken"}, 403 - -	# check if email is taken -	if cursor.execute("select email from users where email = ?", -						[email]).fetchone(): -		return {"error": "email_taken"}, 403 - -	# create new user_id, hash password and note timestamp -	user_id = new_uuid("users") -	password_hash = passwords.password_hash(password) -	registered = int(time.time() * 1000) - -	# write new user to database and commit -	cursor.execute( -		"insert into users values (?, ?, ?, NULL, NULL, ?, ?, \"[]\", FALSE, \"user\", \"{}\", \"online\") ", -		(user_id, username, email, password_hash, registered) -	) -	connection.commit() - -	# create a new token for the user to use for authentication -	new_token = token.generate_token() -	token.add_token(user_id, token.hash_token(new_token)) - -	# create a flask response object to add the set-cookie header to -	res = make_response("", 200) -	res.set_cookie( -		"token", -		new_token["token"], -		expires=int(new_token["expirationDate"] / 1000) -	) - -	return res +    # parse request data if the content-type header is set to application/json +    data = request.get_json() + +    # force string if {}.get(...) returns None +    username = data.get("username") or "" +    email = data.get("email") or "" +    password = data.get("password") or "" + +    # return 400 (malformed request) if any of the required data is missing +    if not username or \ +                                        not email or \ +                                        not password: +        return "", 400 + +    # return 403 (forbidden) if any of the required data is invalid +    if not validate_username(username) or \ +                                        not validate_email(email) or \ +                                        not validate_password(password): +        return {"error": "form_data_invalid"}, 403 + +    # check if username is taken +    if cursor.execute( +        "select username from users where lower(username) = lower(?)", +        [username] +    ).fetchone(): +        return {"error": "username_taken"}, 403 + +    # check if email is taken +    if cursor.execute("select email from users where email = ?", +                      [email]).fetchone(): +        return {"error": "email_taken"}, 403 + +    # create new user_id, hash password and note timestamp +    user_id = new_uuid("users") +    password_hash = passwords.password_hash(password) +    registered = int(time.time() * 1000) + +    # write new user to database and commit +    cursor.execute( +        "insert into users values (?, ?, ?, NULL, NULL, ?, ?, \"[]\", FALSE, \"user\", \"{}\", \"online\") ", +        (user_id, username, email, password_hash, registered) +    ) +    connection.commit() + +    # create a new token for the user to use for authentication +    new_token = token.generate_token() +    token.add_token(user_id, token.hash_token(new_token)) + +    # create a flask response object to add the set-cookie header to +    res = make_response("", 200) +    res.set_cookie( +        "token", +        new_token["token"], +        expires=int(new_token["expirationDate"] / 1000) +    ) + +    return res  dynamic_route = ["/auth", signup] diff --git a/api/auth/token.py b/api/auth/token.py index e94b014..d439924 100644 --- a/api/auth/token.py +++ b/api/auth/token.py @@ -7,54 +7,54 @@ import time  # get valid token hashes for a given user_id  def valid_tokens(user_id): -	tokens = json.loads( -		cursor.execute( -			"select valid_tokens from users where user_id = ?", [user_id] -		).fetchone()[0] -	) -	# return only tokens that aren't expired -	return [ -		token for token in tokens -		if token["expirationDate"] > int(time.time() * 1000) -	] +    tokens = json.loads( +        cursor.execute( +            "select valid_tokens from users where user_id = ?", [user_id] +        ).fetchone()[0] +    ) +    # return only tokens that aren't expired +    return [ +        token for token in tokens +        if token["expirationDate"] > int(time.time() * 1000) +    ]  def validate_token(user_id, token): -	tokens = valid_tokens(user_id) -	return hashlib.sha256(str(token).encode()).hexdigest() in [ -		t["token"] for t in tokens -		if t["expirationDate"] > int(time.time() * 1000) -	] +    tokens = valid_tokens(user_id) +    return hashlib.sha256(str(token).encode()).hexdigest() in [ +        t["token"] for t in tokens +        if t["expirationDate"] > int(time.time() * 1000) +    ]  def modify_tokens(user_id, formatted_token, remove): -	temp_tokens = valid_tokens(user_id) -	temp_tokens.remove(formatted_token -						) if remove else temp_tokens.append(formatted_token) -	cursor.execute( -		"update users set valid_tokens = ? where user_id = ?", -		[json.dumps(temp_tokens), user_id] -	) -	connection.commit() +    temp_tokens = valid_tokens(user_id) +    temp_tokens.remove(formatted_token +                       ) if remove else temp_tokens.append(formatted_token) +    cursor.execute( +        "update users set valid_tokens = ? where user_id = ?", +        [json.dumps(temp_tokens), user_id] +    ) +    connection.commit()  def add_token(user_id, formatted_token): -	modify_tokens(user_id, formatted_token, False) +    modify_tokens(user_id, formatted_token, False)  def revoke_token(user_id, formatted_token): -	modify_tokens(user_id, formatted_token, True) +    modify_tokens(user_id, formatted_token, True)  def hash_token(token): -	return { -		"token": hashlib.sha256(str(token["token"]).encode()).hexdigest(), -		"expirationDate": token["expirationDate"] -	} +    return { +        "token": hashlib.sha256(str(token["token"]).encode()).hexdigest(), +        "expirationDate": token["expirationDate"] +    }  def generate_token(): -	return { -		"token": secrets.token_hex(128), -		"expirationDate": int(time.time() * 1000) + (24 * 60 * 60 * 1000) -	} +    return { +        "token": secrets.token_hex(128), +        "expirationDate": int(time.time() * 1000) + (24 * 60 * 60 * 1000) +    }  |