This repository has been archived by the owner on Sep 27, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added user authentication; still some todo\'s
- Loading branch information
Showing
10 changed files
with
181 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
[session] | ||
service = https://localhost:8080/api/login | ||
cookie_domain = localhost | ||
max_cookie_age = 86400 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from fastapi import Request, Response | ||
|
||
from controllers.auth.encryption_controller import encrypt, generate_keys | ||
from controllers.properties.Properties import Properties | ||
|
||
props: Properties = Properties() | ||
|
||
|
||
def set_cookies(response: Response, key: str, value: str) -> Response: | ||
value: str = encrypt(value) | ||
max_age: int = int(props.get("session", "max_cookie_age")) | ||
domain: str = props.get("session", "cookie_domain") | ||
response.set_cookie(key=key, | ||
value=value, | ||
max_age=max_age, | ||
domain=domain, | ||
secure=False) | ||
return response | ||
|
||
|
||
def get_cookie(request: Request, key: str) -> str: | ||
return request.cookies.get(key) | ||
|
||
|
||
def delete_cookie(response: Response, cookie_tag: str) -> Response: | ||
response.delete_cookie(cookie_tag) | ||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import base64 | ||
|
||
from cryptography.hazmat.primitives import hashes | ||
from cryptography.hazmat.primitives.asymmetric import padding, rsa | ||
from cryptography.hazmat.primitives.asymmetric.padding import OAEP | ||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey | ||
|
||
# TODO: Use database to manage keys instead of dict | ||
keys: dict = {} | ||
|
||
|
||
def generate_keys(user_id: str) -> RSAPublicKey: | ||
if user_id in keys: | ||
return keys[user_id].public_key() | ||
|
||
private_key: RSAPrivateKey = rsa.generate_private_key( | ||
public_exponent=65537, | ||
key_size=2048, | ||
) | ||
keys[user_id] = private_key | ||
return private_key.public_key() | ||
|
||
|
||
def encrypt(user_id: str) -> str: | ||
key = generate_keys(user_id) | ||
encryption: bytes = key.encrypt( | ||
plaintext=user_id.encode(), | ||
padding=OAEP( | ||
mgf=padding.MGF1(algorithm=hashes.SHA256()), | ||
algorithm=hashes.SHA256(), | ||
label=None, | ||
), | ||
) | ||
return base64.b64encode(encryption).decode() | ||
|
||
|
||
def decrypt(user_id: str, encrypted_text: str) -> str | None: | ||
if user_id in keys: | ||
private_key: RSAPrivateKey = keys[user_id] | ||
encryption: bytes = base64.b64decode(encrypted_text.encode()) | ||
return private_key.decrypt( | ||
encryption, | ||
padding=OAEP( | ||
mgf=padding.MGF1(algorithm=hashes.SHA256()), | ||
algorithm=hashes.SHA256(), | ||
label=None, | ||
), | ||
).decode() | ||
return None | ||
|
||
|
||
def delete_key(user_id: str) -> None: | ||
keys.pop(user_id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from fastapi import Request | ||
|
||
from controllers.auth.cookie_controller import get_cookie | ||
from controllers.auth.encryption_controller import decrypt | ||
from controllers.properties.Properties import Properties | ||
|
||
# test url: https://login.ugent.be/login?service=https://localhost:8080/api/login | ||
props: Properties = Properties() | ||
|
||
|
||
def verify_session(request: Request, user_id: str) -> bool: | ||
session: str = get_cookie(request, "session_id") | ||
if session: | ||
decryption = decrypt(user_id=user_id, encrypted_text=session) | ||
return user_id == decryption if decryption else False | ||
return False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import configparser | ||
|
||
|
||
class Properties: | ||
def __init__(self) -> None: | ||
config = configparser.ConfigParser() | ||
config.read("application.properties") | ||
self.properties: dict = {} | ||
for section in config.sections(): | ||
self.properties[section] = {} | ||
for key, val in config[section].items(): | ||
self.properties[section][key] = val | ||
|
||
def get(self, section: str, key: str) -> str: | ||
return self.properties[section][key] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
from fastapi import APIRouter, Request, Response | ||
from fastapi.responses import JSONResponse | ||
|
||
from controllers.auth.authentication_controller import authenticate_user | ||
from controllers.auth.cookie_controller import delete_cookie, set_cookies | ||
from controllers.auth.encryption_controller import delete_key | ||
from controllers.auth.login_controller import verify_session | ||
|
||
session_router = APIRouter() | ||
|
||
|
||
@session_router.get("/api/login") | ||
def login(ticket: str) -> JSONResponse: | ||
""" | ||
This function start a session for the user. | ||
For authentication, it uses the given ticket and the UGent CAS server (https://login.ugent.be). | ||
:param ticket: A UGent CAS ticket that will be used for the authentication | ||
:return: | ||
- Valid Ticket: A JSONResponse with a user object; a cookie will be set with a session_id | ||
- Invalid Ticket: A JSONResponse with status_code 401 and an error message | ||
""" | ||
user: dict = authenticate_user(ticket) # This should be a user object | ||
if user: | ||
response: JSONResponse = JSONResponse(content=user) | ||
# TODO: Change mail to user id | ||
print("here") | ||
response = set_cookies(response, "session_id", user["mail"]) | ||
return response | ||
return JSONResponse(status_code=401, content="Invalid Ticket") | ||
|
||
|
||
@session_router.get("/api/logout/{user_id}") | ||
def logout(user_id: str) -> Response: | ||
""" | ||
This function will log a user out, by removing the session from storage | ||
:param user_id: An identifier of the user that needs to be logged out | ||
:return: A confirmation that the logout was successful, and it tells the browser to remove the cookie. | ||
""" | ||
response: Response = Response(content="You've been successfully logged out") | ||
|
||
delete_cookie(response, "session_id") | ||
delete_key(user_id) | ||
return response | ||
|
||
|
||
@session_router.get("/api/session/verify/{user_id}") | ||
def verify(request: Request, user_id: str) -> bool: | ||
""" | ||
A test route to check if the user has a valid session | ||
:param request: Http Request filled in by fastapi | ||
:param user_id: identifier for the user | ||
:return: boolean that says if the user is logged in | ||
""" | ||
return verify_session(request, user_id) |
This file was deleted.
Oops, something went wrong.