Implement sessions
- Missing security 💀
Signed-off-by: Tiago Garcia <tiago.rgarcia@ua.pt>
This commit is contained in:
parent
50cd873bdc
commit
a28dc261f5
|
@ -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)
|
|
@ -1,3 +1,4 @@
|
|||
from .user import *
|
||||
from .org import *
|
||||
from .file import *
|
||||
from .file import *
|
||||
from .session import *
|
|
@ -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
|
||||
}
|
|
@ -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]
|
||||
}
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from .orgs import OrganizationService
|
||||
from .users import UserService
|
||||
from .files import FileService
|
||||
from .files import FileService
|
||||
from .sessions import SessionService
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
|
@ -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)
|
||||
|
@ -30,4 +30,13 @@ class UserService:
|
|||
user.orgs.append(org)
|
||||
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
|
Loading…
Reference in New Issue