Skip to content

Commit

Permalink
permissions: multiple filters per permission
Browse files Browse the repository at this point in the history
  • Loading branch information
bouttier committed Apr 20, 2023
1 parent 2c57261 commit bb568e3
Show file tree
Hide file tree
Showing 10 changed files with 463 additions and 156 deletions.
6 changes: 3 additions & 3 deletions backend/geonature/core/gn_commons/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from geonature.core.admin.utils import CruvedProtectedMixin
from geonature.core.gn_commons.models import TModules
from geonature.core.gn_permissions.models import TObjects
from geonature.core.gn_permissions.models import PermObject
from geonature.core.gn_commons.schemas import TAdditionalFieldsSchema
from geonature.utils.env import DB

Expand Down Expand Up @@ -86,8 +86,8 @@ class BibFieldAdmin(CruvedProtectedMixin, ModelView):
)
},
"objects": {
"query_factory": lambda: DB.session.query(TObjects).filter(
TObjects.code_object.in_(
"query_factory": lambda: DB.session.query(PermObject).filter(
PermObject.code_object.in_(
current_app.config["ADDITIONAL_FIELDS"]["IMPLEMENTED_OBJECTS"]
)
)
Expand Down
6 changes: 3 additions & 3 deletions backend/geonature/core/gn_commons/models/additional_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from .base import cor_field_module, cor_field_object, cor_field_dataset
from geonature.core.gn_meta.models import TDatasets
from geonature.core.gn_permissions.models import TObjects
from geonature.core.gn_permissions.models import PermObject


@serializable
Expand Down Expand Up @@ -49,8 +49,8 @@ class TAdditionalFields(DB.Model):
"TModules",
secondary=cor_field_module,
)
objects = DB.relationship("TObjects", secondary=cor_field_object)
datasets = DB.relationship("TDatasets", secondary=cor_field_dataset)
objects = DB.relationship(PermObject, secondary=cor_field_object)
datasets = DB.relationship(TDatasets, secondary=cor_field_dataset)

def __str__(self):
return f"{self.field_label} ({self.description})"
2 changes: 1 addition & 1 deletion backend/geonature/core/gn_commons/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class TModules(DB.Model):
meta_update_date = DB.Column(DB.DateTime)

objects = DB.relationship(
"TObjects", secondary=lambda: _resolve_import_cor_object_module(), backref="modules"
"PermObject", secondary=lambda: _resolve_import_cor_object_module(), backref="modules"
)
# relationship datasets add via backref

Expand Down
1 change: 0 additions & 1 deletion backend/geonature/core/gn_commons/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
)
from geonature.core.gn_commons.repositories import TMediaRepository
from geonature.core.gn_commons.repositories import get_table_location_id
from geonature.core.gn_permissions.models import TObjects
from geonature.utils.env import DB, db, BACKEND_DIR
from geonature.utils.config import config_frontend, config
from geonature.core.gn_permissions import decorators as permissions
Expand Down
139 changes: 51 additions & 88 deletions backend/geonature/core/gn_permissions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,134 +8,97 @@
from utils_flask_sqla.serializers import serializable
from pypnusershub.db.models import User

from geonature.utils.env import DB
from geonature.utils.env import db


@serializable
class BibFiltersType(DB.Model):
class PermFilterType(db.Model):
__tablename__ = "bib_filters_type"
__table_args__ = {"schema": "gn_permissions"}
id_filter_type = DB.Column(DB.Integer, primary_key=True)
code_filter_type = DB.Column(DB.Unicode)
label_filter_type = DB.Column(DB.Unicode)
description_filter_type = DB.Column(DB.Unicode)
id_filter_type = db.Column(db.Integer, primary_key=True)
code_filter_type = db.Column(db.Unicode)
label_filter_type = db.Column(db.Unicode)
description_filter_type = db.Column(db.Unicode)


@serializable
class TFilters(DB.Model):
__tablename__ = "t_filters"
class PermScope(db.Model):
__tablename__ = "bib_filters_scope"
__table_args__ = {"schema": "gn_permissions"}
id_filter = DB.Column(DB.Integer, primary_key=True)
value_filter = DB.Column(DB.Unicode)
label_filter = DB.Column(DB.Unicode)
description_filter = DB.Column(DB.Unicode)
id_filter_type = DB.Column(DB.Integer, ForeignKey(BibFiltersType.id_filter_type))
filter_type = DB.relationship(BibFiltersType)
value = db.Column(db.Integer, primary_key=True)
label = db.Column(db.Unicode)
description = db.Column(db.Unicode)


@serializable
class TActions(DB.Model):
__tablename__ = "t_actions"
class PermAction(db.Model):
__tablename__ = "bib_actions"
__table_args__ = {"schema": "gn_permissions"}
id_action = DB.Column(DB.Integer, primary_key=True)
code_action = DB.Column(DB.Unicode)
description_action = DB.Column(DB.Unicode)
id_action = db.Column(db.Integer, primary_key=True)
code_action = db.Column(db.Unicode)
description_action = db.Column(db.Unicode)


cor_object_module = DB.Table(
cor_object_module = db.Table(
"cor_object_module",
DB.Column(
db.Column(
"id_cor_object_module",
DB.Integer,
db.Integer,
primary_key=True,
),
DB.Column(
db.Column(
"id_object",
DB.Integer,
db.Integer,
ForeignKey("gn_permissions.t_objects.id_object"),
),
DB.Column(
db.Column(
"id_module",
DB.Integer,
db.Integer,
ForeignKey("gn_commons.t_modules.id_module"),
),
schema="gn_permissions",
)


@serializable
class TObjects(DB.Model):
class PermObject(db.Model):
__tablename__ = "t_objects"
__table_args__ = {"schema": "gn_permissions"}
id_object = DB.Column(DB.Integer, primary_key=True)
code_object = DB.Column(DB.Unicode)
description_object = DB.Column(DB.Unicode)
id_object = db.Column(db.Integer, primary_key=True)
code_object = db.Column(db.Unicode)
description_object = db.Column(db.Unicode)

def __str__(self):
return f"{self.code_object} ({self.description_object})"


@serializable
class CorRoleActionFilterModuleObject(DB.Model):
__tablename__ = "cor_role_action_filter_module_object"
__table_args__ = {"schema": "gn_permissions"}
id_permission = DB.Column(DB.Integer, primary_key=True)
id_role = DB.Column(DB.Integer, ForeignKey("utilisateurs.t_roles.id_role"))
id_action = DB.Column(DB.Integer, ForeignKey("gn_permissions.t_actions.id_action"))
id_filter = DB.Column(DB.Integer, ForeignKey("gn_permissions.t_filters.id_filter"))
id_module = DB.Column(DB.Integer, ForeignKey("gn_commons.t_modules.id_module"))
id_object = DB.Column(
DB.Integer,
ForeignKey("gn_permissions.t_objects.id_object"),
default=select([TObjects.id_object]).where(TObjects.code_object == "ALL"),
)
# compat.
TObjects = PermObject

role = DB.relationship(User, primaryjoin=(User.id_role == id_role), foreign_keys=[id_role])

action = DB.relationship(
TActions,
primaryjoin=(TActions.id_action == id_action),
foreign_keys=[id_action],
@serializable
class Permission(db.Model):
__tablename__ = "t_permissions"
__table_args__ = {"schema": "gn_permissions"}
id_permission = db.Column(db.Integer, primary_key=True)
id_role = db.Column(db.Integer, ForeignKey("utilisateurs.t_roles.id_role"))
id_action = db.Column(db.Integer, ForeignKey(PermAction.id_action))
id_module = db.Column(db.Integer, ForeignKey("gn_commons.t_modules.id_module"))
id_object = db.Column(
db.Integer,
ForeignKey(PermObject.id_object),
default=select([PermObject.id_object]).where(PermObject.code_object == "ALL"),
)

filter = DB.relationship(
TFilters,
primaryjoin=(TFilters.id_filter == id_filter),
foreign_keys=[id_filter],
)
role = db.relationship(User)
action = db.relationship(PermAction)
module = db.relationship("TModules")
object = db.relationship(PermObject)

module = DB.relationship("TModules")
object = DB.relationship("TObjects")

def is_permission_already_exist(
self, id_role, id_action, id_module, id_filter_type, id_object=1
):
"""
Tell if a permission exist for a user, an action, a module and a filter_type
Return:
A CorRoleActionFilterModuleObject if exist or None
"""
privilege = {
"id_role": id_role,
"id_action": id_action,
"id_module": id_module,
"id_object": id_object,
}
return (
DB.session.query(CorRoleActionFilterModuleObject)
.filter_by(**privilege)
.join(TFilters, TFilters == CorRoleActionFilterModuleObject.id_filter)
.join(BibFiltersType, BibFiltersType.id_filter_type == TFilters.id_filter)
.filter(BibFiltersType.id_filter_type == id_filter_type)
.first()
)
scope_value = db.Column(db.Integer, ForeignKey(PermScope.value), nullable=True)
scope = db.relationship(PermScope)

def __str__(self):
return (
f"Permission("
f"id_role={self.id_role},"
f"action={self.action},"
f"filter={self.filter},"
f"module={self.module},"
f"object={self.object})"
)
def has_other_filters_than(self, *args):
if scope_value is not None and "SCOPE" not in args:
return True
return False
32 changes: 16 additions & 16 deletions backend/geonature/core/gn_permissions/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

from geonature.core.gn_commons.models import TModules
from geonature.core.gn_permissions.models import (
CorRoleActionFilterModuleObject,
TFilters,
TActions,
TObjects,
PermAction,
PermObject,
PermScope,
Permission,
)
from geonature.utils.env import db

Expand All @@ -18,32 +18,29 @@

def _get_user_permissions(id_role):
default_module = TModules.query.filter_by(module_code="GEONATURE").one()
default_object = TObjects.query.filter_by(code_object="ALL").one()
default_object = PermObject.query.filter_by(code_object="ALL").one()
return (
CorRoleActionFilterModuleObject.query.options(
joinedload(CorRoleActionFilterModuleObject.action),
joinedload(CorRoleActionFilterModuleObject.filter).joinedload(TFilters.filter_type),
Permission.query.options(
joinedload(Permission.action),
)
.filter(
sa.or_(
# direct permissions
CorRoleActionFilterModuleObject.id_role == id_role,
Permission.id_role == id_role,
# permissions through group
CorRoleActionFilterModuleObject.role.has(
User.members.any(User.id_role == id_role)
),
Permission.role.has(User.members.any(User.id_role == id_role)),
),
)
# These ordering ensure groupby is working properly
.order_by(CorRoleActionFilterModuleObject.id_action)
.order_by(Permission.id_action)
.all()
)


def _get_user_permissions_by_action(id_role):
permissions = _get_user_permissions(id_role)
# This ensures empty permissions list for action without permissions
permissions_by_action = {action.code_action: [] for action in TActions.query.all()}
permissions_by_action = {action.code_action: [] for action in PermAction.query.all()}
# Note: groupby require sorted data, which is done at SQL level
permissions_by_action.update(
{
Expand Down Expand Up @@ -107,9 +104,12 @@ def get_scope(action_code, id_role=None, module_code=None, object_code=None):
permissions = get_permissions(action_code, id_role, module_code, object_code)
max_scope = 0
for permission in permissions:
if permission.filter.filter_type.code_filter_type != "SCOPE":
if permission.has_other_filters_than("SCOPE"):
continue
max_scope = max(max_scope, int(permission.filter.value_filter))
if permission.scope_value is None:
max_scope = 3
else:
max_scope = max(max_scope, permission.scope_value)
return max_scope


Expand Down
Loading

0 comments on commit bb568e3

Please sign in to comment.