1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-28 11:18:11 +00:00

New database schema for better RBAC

This commit is contained in:
grossmj 2023-08-21 21:32:23 +10:00
parent 74cb3be910
commit 6bd855b3c5
8 changed files with 114 additions and 21 deletions

View File

@ -16,6 +16,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from .base import Base from .base import Base
from .acl import ACL
from .resources import Resource
from .users import User, UserGroup from .users import User, UserGroup
from .roles import Role from .roles import Role
from .permissions import Permission from .permissions import Permission

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python
#
# Copyright (C) 2023 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sqlalchemy import Column, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from .base import BaseTable, generate_uuid, GUID
import logging
log = logging.getLogger(__name__)
class ACL(BaseTable):
__tablename__ = "acl"
acl_id = Column(GUID, primary_key=True, default=generate_uuid)
allowed = Column(Boolean, default=True)
user_id = Column(GUID, ForeignKey('users.user_id', ondelete="CASCADE"))
user = relationship("User", back_populates="acl_entries")
group_id = Column(GUID, ForeignKey('user_groups.user_group_id', ondelete="CASCADE"))
group = relationship("UserGroup", back_populates="acl_entries")
resource_id = Column(GUID, ForeignKey('resources.resource_id', ondelete="CASCADE"))
resource = relationship("Resource", back_populates="acl_entries")
role_id = Column(GUID, ForeignKey('roles.role_id', ondelete="CASCADE"))
role = relationship("Role", back_populates="acl_entries")

View File

@ -21,8 +21,8 @@ from sqlalchemy.orm import relationship
from .base import Base, BaseTable, GUID from .base import Base, BaseTable, GUID
image_template_link = Table( image_template_map = Table(
"images_templates_link", "image_template_map",
Base.metadata, Base.metadata,
Column("image_id", Integer, ForeignKey("images.image_id", ondelete="CASCADE")), Column("image_id", Integer, ForeignKey("images.image_id", ondelete="CASCADE")),
Column("template_id", GUID, ForeignKey("templates.template_id", ondelete="CASCADE")) Column("template_id", GUID, ForeignKey("templates.template_id", ondelete="CASCADE"))
@ -40,4 +40,4 @@ class Image(BaseTable):
image_size = Column(BigInteger) image_size = Column(BigInteger)
checksum = Column(String, index=True) checksum = Column(String, index=True)
checksum_algorithm = Column(String) checksum_algorithm = Column(String)
templates = relationship("Template", secondary=image_template_link, back_populates="images") templates = relationship("Template", secondary=image_template_map, back_populates="images")

View File

@ -25,8 +25,8 @@ import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
permission_role_link = Table( permission_role_map = Table(
"permissions_roles_link", "permission_role_map",
Base.metadata, Base.metadata,
Column("permission_id", GUID, ForeignKey("permissions.permission_id", ondelete="CASCADE")), Column("permission_id", GUID, ForeignKey("permissions.permission_id", ondelete="CASCADE")),
Column("role_id", GUID, ForeignKey("roles.role_id", ondelete="CASCADE")) Column("role_id", GUID, ForeignKey("roles.role_id", ondelete="CASCADE"))
@ -44,7 +44,8 @@ class Permission(BaseTable):
path = Column(String) path = Column(String)
action = Column(String) action = Column(String)
user_id = Column(GUID, ForeignKey('users.user_id', ondelete="CASCADE")) user_id = Column(GUID, ForeignKey('users.user_id', ondelete="CASCADE"))
roles = relationship("Role", secondary=permission_role_link, back_populates="permissions") user = relationship("User", back_populates="permissions")
roles = relationship("Role", secondary=permission_role_map, back_populates="permissions")
@event.listens_for(Permission.__table__, 'after_create') @event.listens_for(Permission.__table__, 'after_create')
@ -95,7 +96,7 @@ def create_default_roles(target, connection, **kw):
log.debug("The default permissions have been created in the database") log.debug("The default permissions have been created in the database")
@event.listens_for(permission_role_link, 'after_create') @event.listens_for(permission_role_map, 'after_create')
def add_permissions_to_role(target, connection, **kw): def add_permissions_to_role(target, connection, **kw):
from .roles import Role from .roles import Role

View File

@ -0,0 +1,45 @@
#!/usr/bin/env python
#
# Copyright (C) 2023 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sqlalchemy import Column, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from .base import BaseTable, generate_uuid, GUID
import logging
log = logging.getLogger(__name__)
class Resource(BaseTable):
__tablename__ = "resources"
resource_id = Column(GUID, primary_key=True, default=generate_uuid)
name = Column(String, unique=True, index=True)
description = Column(String)
propagate = Column(Boolean, default=True)
user_id = Column(GUID, ForeignKey('users.user_id', ondelete="CASCADE"))
user = relationship("User", back_populates="resources")
acl_entries = relationship("ACL")
parent_id = Column(GUID, ForeignKey("resources.resource_id", ondelete="CASCADE"))
children = relationship(
"Resource",
remote_side=[resource_id],
cascade="all, delete-orphan",
single_parent=True
)

View File

@ -19,14 +19,14 @@ from sqlalchemy import Table, Column, String, Boolean, ForeignKey, event
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from .base import Base, BaseTable, generate_uuid, GUID from .base import Base, BaseTable, generate_uuid, GUID
from .permissions import permission_role_link from .permissions import permission_role_map
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
role_group_link = Table( role_group_map = Table(
"roles_groups_link", "role_group_map",
Base.metadata, Base.metadata,
Column("role_id", GUID, ForeignKey("roles.role_id", ondelete="CASCADE")), Column("role_id", GUID, ForeignKey("roles.role_id", ondelete="CASCADE")),
Column("user_group_id", GUID, ForeignKey("user_groups.user_group_id", ondelete="CASCADE")) Column("user_group_id", GUID, ForeignKey("user_groups.user_group_id", ondelete="CASCADE"))
@ -41,8 +41,9 @@ class Role(BaseTable):
name = Column(String, unique=True, index=True) name = Column(String, unique=True, index=True)
description = Column(String) description = Column(String)
is_builtin = Column(Boolean, default=False) is_builtin = Column(Boolean, default=False)
permissions = relationship("Permission", secondary=permission_role_link, back_populates="roles") permissions = relationship("Permission", secondary=permission_role_map, back_populates="roles")
groups = relationship("UserGroup", secondary=role_group_link, back_populates="roles") groups = relationship("UserGroup", secondary=role_group_map, back_populates="roles")
acl_entries = relationship("ACL")
@event.listens_for(Role.__table__, 'after_create') @event.listens_for(Role.__table__, 'after_create')
@ -59,7 +60,7 @@ def create_default_roles(target, connection, **kw):
log.debug("The default roles have been created in the database") log.debug("The default roles have been created in the database")
@event.listens_for(role_group_link, 'after_create') @event.listens_for(role_group_map, 'after_create')
def add_admin_to_group(target, connection, **kw): def add_admin_to_group(target, connection, **kw):
from .users import UserGroup from .users import UserGroup

View File

@ -20,7 +20,7 @@ from sqlalchemy import Boolean, Column, String, Integer, ForeignKey, PickleType
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from .base import BaseTable, generate_uuid, GUID from .base import BaseTable, generate_uuid, GUID
from .images import image_template_link from .images import image_template_map
class Template(BaseTable): class Template(BaseTable):
@ -37,7 +37,7 @@ class Template(BaseTable):
usage = Column(String) usage = Column(String)
template_type = Column(String) template_type = Column(String)
compute_id = Column(String) compute_id = Column(String)
images = relationship("Image", secondary=image_template_link, back_populates="templates") images = relationship("Image", secondary=image_template_map, back_populates="templates")
__mapper_args__ = { __mapper_args__ = {
"polymorphic_identity": "templates", "polymorphic_identity": "templates",

View File

@ -19,7 +19,7 @@ from sqlalchemy import Table, Boolean, Column, String, DateTime, ForeignKey, eve
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from .base import Base, BaseTable, generate_uuid, GUID from .base import Base, BaseTable, generate_uuid, GUID
from .roles import role_group_link from .roles import role_group_map
from gns3server.config import Config from gns3server.config import Config
from gns3server.services import auth_service from gns3server.services import auth_service
@ -28,8 +28,8 @@ import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
user_group_link = Table( user_group_map = Table(
"users_groups_link", "user_group_map",
Base.metadata, Base.metadata,
Column("user_id", GUID, ForeignKey("users.user_id", ondelete="CASCADE")), Column("user_id", GUID, ForeignKey("users.user_id", ondelete="CASCADE")),
Column("user_group_id", GUID, ForeignKey("user_groups.user_group_id", ondelete="CASCADE")) Column("user_group_id", GUID, ForeignKey("user_groups.user_group_id", ondelete="CASCADE"))
@ -48,8 +48,10 @@ class User(BaseTable):
last_login = Column(DateTime) last_login = Column(DateTime)
is_active = Column(Boolean, default=True) is_active = Column(Boolean, default=True)
is_superadmin = Column(Boolean, default=False) is_superadmin = Column(Boolean, default=False)
groups = relationship("UserGroup", secondary=user_group_link, back_populates="users") groups = relationship("UserGroup", secondary=user_group_map, back_populates="users")
resources = relationship("Resource")
permissions = relationship("Permission") permissions = relationship("Permission")
acl_entries = relationship("ACL")
@event.listens_for(User.__table__, 'after_create') @event.listens_for(User.__table__, 'after_create')
@ -77,8 +79,9 @@ class UserGroup(BaseTable):
user_group_id = Column(GUID, primary_key=True, default=generate_uuid) user_group_id = Column(GUID, primary_key=True, default=generate_uuid)
name = Column(String, unique=True, index=True) name = Column(String, unique=True, index=True)
is_builtin = Column(Boolean, default=False) is_builtin = Column(Boolean, default=False)
users = relationship("User", secondary=user_group_link, back_populates="groups") users = relationship("User", secondary=user_group_map, back_populates="groups")
roles = relationship("Role", secondary=role_group_link, back_populates="groups") roles = relationship("Role", secondary=role_group_map, back_populates="groups")
acl_entries = relationship("ACL")
@event.listens_for(UserGroup.__table__, 'after_create') @event.listens_for(UserGroup.__table__, 'after_create')