Implement perms system
Signed-off-by: Tiago Garcia <tiago.rgarcia@ua.pt>
This commit is contained in:
parent
ee825dcf39
commit
0ce6d25359
|
@ -5,15 +5,15 @@ class Organization(db_connection.Model):
|
||||||
|
|
||||||
id = db_connection.Column(db_connection.Integer, primary_key=True, index=True)
|
id = db_connection.Column(db_connection.Integer, primary_key=True, index=True)
|
||||||
name = db_connection.Column(db_connection.String, unique=True, index=True, nullable=False)
|
name = db_connection.Column(db_connection.String, unique=True, index=True, nullable=False)
|
||||||
|
roles = db_connection.Column(db_connection.JSON, nullable=False, default=dict)
|
||||||
users = db_connection.Column(db_connection.JSON, nullable=False, default=dict)
|
users = db_connection.Column(db_connection.JSON, nullable=False, default=dict)
|
||||||
users_id = db_connection.Column(db_connection.Integer, db_connection.ForeignKey('users.id'))
|
users_id = db_connection.Column(db_connection.Integer, db_connection.ForeignKey('users.id'))
|
||||||
manager = db_connection.relationship('User', backref=db_connection.backref('owned_organization', uselist=False))
|
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
return {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"manager": self.manager.to_dict(),
|
"roles": [{"role": role, "permissions": permissions} for role, permissions in self.roles.items()],
|
||||||
"users": [{"id": user_id, "user_data": {
|
"users": [{"id": user_id, "user_data": {
|
||||||
"username": user_data["username"],
|
"username": user_data["username"],
|
||||||
"full_name": user_data["full_name"],
|
"full_name": user_data["full_name"],
|
||||||
|
|
|
@ -9,6 +9,7 @@ class User(db_connection.Model):
|
||||||
username = db_connection.Column(db_connection.String, unique=True, index=True, nullable=False)
|
username = db_connection.Column(db_connection.String, unique=True, index=True, nullable=False)
|
||||||
full_name = db_connection.Column(db_connection.String, 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)
|
email = db_connection.Column(db_connection.String, unique=True, index=True, nullable=False)
|
||||||
|
roles = db_connection.Column(db_connection.JSON, nullable=False, default=dict)
|
||||||
public_keys = db_connection.Column(db_connection.JSON, nullable=False, default=dict)
|
public_keys = db_connection.Column(db_connection.JSON, nullable=False, default=dict)
|
||||||
orgs = db_connection.Column(db_connection.JSON, nullable=False, default=dict)
|
orgs = db_connection.Column(db_connection.JSON, nullable=False, default=dict)
|
||||||
|
|
||||||
|
@ -18,5 +19,6 @@ class User(db_connection.Model):
|
||||||
"username": self.username,
|
"username": self.username,
|
||||||
"full_name": self.full_name,
|
"full_name": self.full_name,
|
||||||
"email": self.email,
|
"email": self.email,
|
||||||
|
"role": self.role,
|
||||||
"orgs": [{"id": org_id, "name": org_data["name"], "status": org_data["status"]} for org_id, org_data in self.orgs.items()],
|
"orgs": [{"id": org_id, "name": org_data["name"], "status": org_data["status"]} for org_id, org_data in self.orgs.items()],
|
||||||
}
|
}
|
|
@ -89,9 +89,6 @@ def user_create():
|
||||||
if not org:
|
if not org:
|
||||||
return jsonify({"error": "Organization not found"}), 404
|
return jsonify({"error": "Organization not found"}), 404
|
||||||
|
|
||||||
if org.manager.id != session.user_id:
|
|
||||||
return jsonify({"error": "Not authorized to create users"}), 403
|
|
||||||
|
|
||||||
user = UserService.get_user_by_username(data["username"])
|
user = UserService.get_user_by_username(data["username"])
|
||||||
if not user:
|
if not user:
|
||||||
user = UserService.create_user(
|
user = UserService.create_user(
|
||||||
|
@ -118,8 +115,6 @@ def user_suspend(username):
|
||||||
org = OrganizationService.get_organization(session.org_id)
|
org = OrganizationService.get_organization(session.org_id)
|
||||||
if not org:
|
if not org:
|
||||||
return jsonify({"error": "Organization not found"}), 404
|
return jsonify({"error": "Organization not found"}), 404
|
||||||
if org.manager.id != session.user_id:
|
|
||||||
return jsonify({"error": "Not authorized to suspend users"}), 403
|
|
||||||
|
|
||||||
user = UserService.get_user_by_username(username)
|
user = UserService.get_user_by_username(username)
|
||||||
if not user:
|
if not user:
|
||||||
|
@ -141,8 +136,6 @@ def user_unsuspend(username):
|
||||||
org = OrganizationService.get_organization(session.org_id)
|
org = OrganizationService.get_organization(session.org_id)
|
||||||
if not org:
|
if not org:
|
||||||
return jsonify({"error": "Organization not found"}), 404
|
return jsonify({"error": "Organization not found"}), 404
|
||||||
if org.manager.id != session.user_id:
|
|
||||||
return jsonify({"error": "Not authorized to unsuspend users"}), 403
|
|
||||||
|
|
||||||
user = UserService.get_user_by_username(username)
|
user = UserService.get_user_by_username(username)
|
||||||
if not user:
|
if not user:
|
||||||
|
|
|
@ -3,6 +3,7 @@ import os.path
|
||||||
from database import db
|
from database import db
|
||||||
from models import Organization, User
|
from models import Organization, User
|
||||||
from sqlalchemy.orm.attributes import flag_modified
|
from sqlalchemy.orm.attributes import flag_modified
|
||||||
|
from utils.perms import Perm
|
||||||
|
|
||||||
|
|
||||||
class OrganizationService:
|
class OrganizationService:
|
||||||
|
@ -18,9 +19,33 @@ class OrganizationService:
|
||||||
if not os.path.exists(os.path.join(repos, name)):
|
if not os.path.exists(os.path.join(repos, name)):
|
||||||
os.mkdir(os.path.join(repos, name))
|
os.mkdir(os.path.join(repos, name))
|
||||||
|
|
||||||
|
roles = {
|
||||||
|
"manager": {
|
||||||
|
"permissions": Perm.get_int([
|
||||||
|
Perm.DOC_ACL,
|
||||||
|
Perm.DOC_READ,
|
||||||
|
Perm.DOC_DELETE,
|
||||||
|
Perm.ROLE_ACL,
|
||||||
|
Perm.SUBJECT_NEW,
|
||||||
|
Perm.SUBJECT_DOWN,
|
||||||
|
Perm.SUBJECT_UP,
|
||||||
|
Perm.DOC_NEW,
|
||||||
|
Perm.ROLE_NEW,
|
||||||
|
Perm.ROLE_DOWN,
|
||||||
|
Perm.ROLE_UP,
|
||||||
|
Perm.ROLE_MOD
|
||||||
|
]),
|
||||||
|
"users": [user.id]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"permissions": Perm.get_int([]),
|
||||||
|
"users": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
organization = Organization(
|
organization = Organization(
|
||||||
name=name,
|
name=name,
|
||||||
manager=user,
|
roles=roles,
|
||||||
users={user.id: {
|
users={user.id: {
|
||||||
"username": user.username,
|
"username": user.username,
|
||||||
"full_name": user.full_name,
|
"full_name": user.full_name,
|
||||||
|
@ -34,6 +59,7 @@ class OrganizationService:
|
||||||
db.refresh(organization)
|
db.refresh(organization)
|
||||||
|
|
||||||
UserService().add_org_to_user(user, organization)
|
UserService().add_org_to_user(user, organization)
|
||||||
|
UserService().add_role_to_user(user, organization, "manager")
|
||||||
UserService().add_public_key_to_user(user, organization, public_key)
|
UserService().add_public_key_to_user(user, organization, public_key)
|
||||||
|
|
||||||
return organization
|
return organization
|
||||||
|
@ -71,12 +97,40 @@ class OrganizationService:
|
||||||
db.refresh(org)
|
db.refresh(org)
|
||||||
return org
|
return org
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_role(org: Organization, role: str, perms: list[Perm]) -> Organization:
|
||||||
|
roles = org.roles.copy()
|
||||||
|
roles[role] = {
|
||||||
|
"permissions": Perm.get_int(perms),
|
||||||
|
"users": []
|
||||||
|
}
|
||||||
|
org.roles = roles
|
||||||
|
flag_modified(org, "roles")
|
||||||
|
db.commit()
|
||||||
|
db.refresh(org)
|
||||||
|
return org
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete_role(org: Organization, role: str) -> Organization:
|
||||||
|
roles = org.roles.copy()
|
||||||
|
del roles[role]
|
||||||
|
org.roles = roles
|
||||||
|
flag_modified(org, "roles")
|
||||||
|
db.commit()
|
||||||
|
db.refresh(org)
|
||||||
|
return org
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_role_permission(org: Organization, role: str, perm: Perm) -> bool:
|
||||||
|
role_perms = org.roles[role]["permissions"]
|
||||||
|
return Perm.check_perm(role_perms, perm.value)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def suspend_user(org: Organization, user: User) -> tuple:
|
def suspend_user(org: Organization, user: User) -> tuple:
|
||||||
if OrganizationService.get_user_status(org, user.id) != "active":
|
if OrganizationService.get_user_status(org, user.id) != "active":
|
||||||
return {"error": "User already suspended"}, 400
|
return {"error": "User already suspended"}, 400
|
||||||
|
|
||||||
if org.manager.id == user.id:
|
if user.roles[org.id] == "manager":
|
||||||
return {"error": "Cannot suspend manager"}, 400
|
return {"error": "Cannot suspend manager"}, 400
|
||||||
|
|
||||||
org.users[str(user.id)]["status"] = "suspended"
|
org.users[str(user.id)]["status"] = "suspended"
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from sqlalchemy.orm.attributes import flag_modified
|
||||||
|
|
||||||
from database import db
|
from database import db
|
||||||
from models import User, Organization
|
from models import User, Organization
|
||||||
|
|
||||||
|
@ -10,6 +12,7 @@ class UserService:
|
||||||
username=username,
|
username=username,
|
||||||
full_name=full_name,
|
full_name=full_name,
|
||||||
email=email,
|
email=email,
|
||||||
|
roles={},
|
||||||
public_keys={org.id: public_key} if org else {},
|
public_keys={org.id: public_key} if org else {},
|
||||||
orgs={org.id: {
|
orgs={org.id: {
|
||||||
"name": org.name,
|
"name": org.name,
|
||||||
|
@ -43,6 +46,40 @@ class UserService:
|
||||||
db.refresh(user)
|
db.refresh(user)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_role_to_user(user: User, org: Organization, role: str) -> User:
|
||||||
|
roles = user.roles.copy()
|
||||||
|
roles[org.id] = role
|
||||||
|
user.roles = roles
|
||||||
|
flag_modified(user, "roles")
|
||||||
|
db.commit()
|
||||||
|
db.refresh(user)
|
||||||
|
|
||||||
|
roles = org.roles.copy()
|
||||||
|
roles[role]["users"].append(user.id)
|
||||||
|
org.roles = roles
|
||||||
|
flag_modified(org, "roles")
|
||||||
|
db.commit()
|
||||||
|
db.refresh(org)
|
||||||
|
return user
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def remove_role_from_user(user: User, org: Organization, role: str) -> User:
|
||||||
|
roles = user.roles.copy()
|
||||||
|
roles.pop(org.id)
|
||||||
|
user.roles = roles
|
||||||
|
flag_modified(user, "roles")
|
||||||
|
db.commit()
|
||||||
|
db.refresh(user)
|
||||||
|
|
||||||
|
roles = org.roles.copy()
|
||||||
|
roles[role]["users"].remove(user.id)
|
||||||
|
org.roles = roles
|
||||||
|
flag_modified(org, "roles")
|
||||||
|
db.commit()
|
||||||
|
db.refresh(org)
|
||||||
|
return user
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_public_key_to_user(user: User, org: Organization, public_key: str) -> User:
|
def add_public_key_to_user(user: User, org: Organization, public_key: str) -> User:
|
||||||
public_keys = user.public_keys.copy()
|
public_keys = user.public_keys.copy()
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class Perm(Enum):
|
||||||
|
DOC_ACL = 0b000000000001
|
||||||
|
DOC_READ = 0b000000000010
|
||||||
|
DOC_DELETE = 0b000000000100
|
||||||
|
ROLE_ACL = 0b000000001000
|
||||||
|
SUBJECT_NEW = 0b000000010000
|
||||||
|
SUBJECT_DOWN = 0b000000100000
|
||||||
|
SUBJECT_UP = 0b000001000000
|
||||||
|
DOC_NEW = 0b000010000000
|
||||||
|
ROLE_NEW = 0b000100000000
|
||||||
|
ROLE_DOWN = 0b001000000000
|
||||||
|
ROLE_UP = 0b010000000000
|
||||||
|
ROLE_MOD = 0b100000000000
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_perm(bit_array: int):
|
||||||
|
for perm in Perm:
|
||||||
|
if bit_array == perm.value:
|
||||||
|
return perm
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_int(perms):
|
||||||
|
if isinstance(perms, list):
|
||||||
|
return sum([p.value for p in perms if isinstance(p, Perm)])
|
||||||
|
return perms.value if isinstance(perms, Perm) else 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_perm(perm, bit_array: int):
|
||||||
|
return perm.value & bit_array == perm.value
|
Loading…
Reference in New Issue