Implement sessions

- Missing security 💀

Signed-off-by: Tiago Garcia <tiago.rgarcia@ua.pt>
This commit is contained in:
Tiago Garcia 2024-11-15 01:18:23 +00:00
parent 50cd873bdc
commit a28dc261f5
Signed by: TiagoRG
GPG Key ID: DFCD48E3F420DB42
10 changed files with 109 additions and 8 deletions

View File

@ -1,7 +1,7 @@
from flask import Flask
from routes import org_bp, user_bp, file_bp
from database import db_connection
from models import Organization, User, File
from models import Organization, User, File, Session
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database.db"
@ -16,5 +16,12 @@ app.register_blueprint(org_bp, url_prefix="/org")
app.register_blueprint(user_bp, url_prefix="/user")
app.register_blueprint(file_bp, url_prefix="/file")
@app.route("/reset")
def reset():
with app.app_context():
db_connection.drop_all()
db_connection.create_all()
return "Database reset"
if __name__ == "__main__":
app.run(debug=True)

View File

@ -1,3 +1,4 @@
from .user import *
from .org import *
from .file import *
from .session import *

View File

@ -0,0 +1,21 @@
from database import db_connection
class Session(db_connection.Model):
__tablename__ = 'sessions'
id = db_connection.Column(db_connection.Integer, primary_key=True)
user_id = db_connection.Column(db_connection.Integer, db_connection.ForeignKey('users.id'))
org_id = db_connection.Column(db_connection.Integer, db_connection.ForeignKey('organizations.id'))
token = db_connection.Column(db_connection.String(255), unique=True)
created_at = db_connection.Column(db_connection.DateTime, server_default=db_connection.func.now())
updated_at = db_connection.Column(db_connection.DateTime, server_default=db_connection.func.now(), server_onupdate=db_connection.func.now())
def to_dict(self):
return {
"id": self.id,
"user_id": self.user_id,
"org_id": self.org_id,
"token": self.token,
"created_at": self.created_at,
"updated_at": self.updated_at
}

View File

@ -1,3 +1,4 @@
from flask_sqlalchemy import SQLAlchemy
from database import db_connection
@ -8,7 +9,7 @@ class User(db_connection.Model):
username = db_connection.Column(db_connection.String, unique=True, index=True, nullable=False)
full_name = db_connection.Column(db_connection.String, nullable=False)
email = db_connection.Column(db_connection.String, unique=True, index=True, nullable=False)
public_key = db_connection.Column(db_connection.String, nullable=False)
public_keys = db_connection.Column(db_connection.JSON, nullable=False, default=dict)
orgs = db_connection.relationship('Organization', back_populates='owner')
files = db_connection.relationship('File', back_populates='creator')
@ -18,7 +19,7 @@ class User(db_connection.Model):
"username": self.username,
"full_name": self.full_name,
"email": self.email,
"public_key": self.public_key,
"public_keys": [{"org_id": org_id, "key": public_key} for org_id, public_key in self.public_keys.items()],
"orgs": [{"id": org.id, "name": org.name} for org in self.orgs],
"files": [{"id": file.id, "name": file.name, "file_handle": file.file_handle} for file in self.files]
}

View File

@ -4,8 +4,15 @@ from services import OrganizationService
org_bp = Blueprint("org", __name__)
@org_bp.route("/create", methods=["POST"])
def create():
def org_create():
data = request.json
if "name" not in data or "username" not in data or "full_name" not in data or "email" not in data or "public_key" not in data:
return jsonify({"error": "Missing required fields"}), 400
existing_org = OrganizationService.get_organization_by_name(data["name"])
if existing_org:
return jsonify({"error": "Organization already exists"}), 400
org = OrganizationService.create_organization(
name=data["name"],
username=data["username"],
@ -13,4 +20,10 @@ def create():
email=data["email"],
public_key=data["public_key"]
)
return jsonify(org.to_dict()), 201
@org_bp.route("/list", methods=["GET"])
def org_list():
orgs = OrganizationService.list_organizations()
return jsonify([org.to_dict() for org in orgs])

View File

@ -1,4 +1,25 @@
from flask import Blueprint, request, jsonify
from services import UserService
from services import UserService, SessionService, OrganizationService
user_bp = Blueprint("user", __name__)
@user_bp.route("/login", methods=["POST"])
def user_login():
data = request.json
user = UserService.get_user_by_username(data["username"])
if not user:
return jsonify({"error": "User not found"}), 404
org = OrganizationService.get_organization_by_name(data["org"])
if not org:
return jsonify({"error": "Organization not found"}), 404
id_str = str(org.id)
if id_str not in user.public_keys:
return jsonify({"error": "User not associated with organization"}), 403
if user.public_keys[id_str] != data["public_key"]:
return jsonify({"error": "Invalid public key"}), 403
session = SessionService.create_session(user, org)
return jsonify(session.to_dict()), 201

View File

@ -1,3 +1,4 @@
from .orgs import OrganizationService
from .users import UserService
from .files import FileService
from .sessions import SessionService

View File

@ -28,6 +28,7 @@ class OrganizationService:
db.refresh(organization)
UserService().add_org_to_user(user, organization)
UserService().add_public_key_to_user(user, organization, public_key)
return organization

View File

@ -0,0 +1,26 @@
import secrets
from database import db
from models import Session, User, Organization
class SessionService:
@staticmethod
def create_session(user: User, org: Organization) -> Session:
session = Session(
user_id=user.id,
org_id=org.id,
token=secrets.token_hex(128)
)
db.add(session)
db.commit()
db.refresh(session)
return session
@staticmethod
def get_session_by_token(token: str) -> Session | None:
return db.query(Session).filter(Session.token == token).first()
@staticmethod
def delete_session(session: Session) -> None:
db.delete(session)
db.commit()

View File

@ -9,7 +9,7 @@ class UserService:
username=username,
full_name=full_name,
email=email,
public_key=public_key,
public_keys={org.id: public_key} if org else {},
orgs=[org] if org else []
)
db.add(user)
@ -31,3 +31,12 @@ class UserService:
db.commit()
db.refresh(user)
return user
@staticmethod
def add_public_key_to_user(user: User, org: Organization, public_key: str) -> User:
public_keys = user.public_keys.copy()
public_keys[org.id] = public_key
user.public_keys = public_keys
db.commit()
db.refresh(user)
return user