Code source de geonature.core.gn_permissions.models

"""
Models of gn_permissions schema
"""

from packaging import version

import sqlalchemy as sa
from sqlalchemy import ForeignKey, ForeignKeyConstraint
from sqlalchemy.sql import select
from sqlalchemy.orm import foreign, joinedload, contains_eager
import flask_sqlalchemy
from utils_flask_sqla.models import qfilter

from utils_flask_sqla.serializers import serializable
from pypnusershub.db.models import User

from geonature.utils.env import db
from geonature.core.gn_commons.models.base import TModules


@serializable
[docs] class PermFilterType(db.Model):
[docs] __tablename__ = "bib_filters_type"
[docs] __table_args__ = {"schema": "gn_permissions"}
[docs] id_filter_type = db.Column(db.Integer, primary_key=True)
[docs] code_filter_type = db.Column(db.Unicode)
[docs] label_filter_type = db.Column(db.Unicode)
[docs] description_filter_type = db.Column(db.Unicode)
@serializable
[docs] class PermScope(db.Model):
[docs] __tablename__ = "bib_filters_scope"
[docs] __table_args__ = {"schema": "gn_permissions"}
[docs] value = db.Column(db.Integer, primary_key=True)
[docs] label = db.Column(db.Unicode)
[docs] description = db.Column(db.Unicode)
[docs] def __str__(self): return self.description
@serializable
[docs] class PermAction(db.Model):
[docs] __tablename__ = "bib_actions"
[docs] __table_args__ = {"schema": "gn_permissions"}
[docs] id_action = db.Column(db.Integer, primary_key=True)
[docs] code_action = db.Column(db.Unicode)
[docs] description_action = db.Column(db.Unicode)
[docs] def __str__(self): return self.description_action
[docs] cor_object_module = db.Table( "cor_object_module", db.Column( "id_cor_object_module", db.Integer, primary_key=True, ), db.Column( "id_object", db.Integer, ForeignKey("gn_permissions.t_objects.id_object"), ), db.Column( "id_module", db.Integer, ForeignKey("gn_commons.t_modules.id_module"), ), schema="gn_permissions", )
@serializable
[docs] class PermObject(db.Model):
[docs] __tablename__ = "t_objects"
[docs] __table_args__ = {"schema": "gn_permissions"}
[docs] id_object = db.Column(db.Integer, primary_key=True)
[docs] code_object = db.Column(db.Unicode)
[docs] description_object = db.Column(db.Unicode)
[docs] def __str__(self): return f"{self.code_object} ({self.description_object})"
# compat.
[docs] TObjects = PermObject
[docs] def _nice_order(model, qs): from geonature.core.gn_commons.models import TModules return ( qs.join(model.module) .join(model.object) .join(model.action) .options( contains_eager(model.module), contains_eager(model.object), contains_eager(model.action), ) .order_by( TModules.module_code, # ensure ALL at first: sa.case([(PermObject.code_object == "ALL", "1")], else_=PermObject.code_object), model.id_action, ) )
[docs] class PermissionAvailable(db.Model):
[docs] __tablename__ = "t_permissions_available"
[docs] __table_args__ = {"schema": "gn_permissions"}
[docs] id_module = db.Column( db.Integer, ForeignKey("gn_commons.t_modules.id_module"), primary_key=True )
[docs] id_object = db.Column( db.Integer, ForeignKey(PermObject.id_object), default=select(PermObject.id_object).where(PermObject.code_object == "ALL"), primary_key=True, )
[docs] id_action = db.Column(db.Integer, ForeignKey(PermAction.id_action), primary_key=True)
[docs] label = db.Column(db.Unicode)
[docs] module = db.relationship("TModules")
[docs] object = db.relationship(PermObject)
[docs] action = db.relationship(PermAction)
[docs] scope_filter = db.Column(db.Boolean, server_default=sa.false())
[docs] sensitivity_filter = db.Column(db.Boolean, server_default=sa.false(), nullable=False)
[docs] filters_fields = { "SCOPE": scope_filter, "SENSITIVITY": sensitivity_filter, }
@property
[docs] def filters(self): return [k for k, v in self.filters_fields.items() if getattr(self, v.name)]
[docs] def __str__(self): s = self.module.module_label if self.object.code_object != "ALL": object_label = self.object.code_object.title().replace("_", " ") s += f" | {object_label}" s += f" | {self.label}" return s
@staticmethod
[docs] def nice_order(**kwargs): # TODO fix when flask admin is compatible with # sqlalchemy2.0 query style query = PermissionAvailable.query return _nice_order(PermissionAvailable, query)
[docs] class PermFilter: def __init__(self, name, value): self.name = name self.value = value
[docs] def __str__(self): if self.name == "SCOPE": if self.value is None: return """<i class="fa fa-users" aria-hidden="true"></i> de tout le monde""" elif self.value == 1: return """<i class="fa fa-user" aria-hidden="true"></i> à moi""" elif self.value == 2: return """<i class="fa fa-user-circle" aria-hidden="true"></i> de mon organisme""" elif self.name == "SENSITIVITY": if self.value: return """<i class="fa fa-low-vision" aria-hidden="true"></i> non sensible""" else: return """<i class="fa fa-eye" aria-hidden="true"></i> sensible et non sensible"""
@serializable
[docs] class Permission(db.Model):
[docs] __tablename__ = "t_permissions"
[docs] __table_args__ = ( ForeignKeyConstraint( ["id_module", "id_object", "id_action"], [ "gn_permissions.t_permissions_available.id_module", "gn_permissions.t_permissions_available.id_object", "gn_permissions.t_permissions_available.id_action", ], ), {"schema": "gn_permissions"}, )
[docs] id_permission = db.Column(db.Integer, primary_key=True)
[docs] id_role = db.Column(db.Integer, ForeignKey("utilisateurs.t_roles.id_role"))
[docs] id_action = db.Column(db.Integer, ForeignKey(PermAction.id_action))
[docs] id_module = db.Column(db.Integer, ForeignKey("gn_commons.t_modules.id_module"))
[docs] id_object = db.Column( db.Integer, ForeignKey(PermObject.id_object), default=select(PermObject.id_object).where(PermObject.code_object == "ALL"), )
[docs] role = db.relationship(User, backref=db.backref("permissions", cascade_backrefs=False))
[docs] action = db.relationship(PermAction)
[docs] module = db.relationship(TModules)
[docs] object = db.relationship(PermObject)
[docs] scope_value = db.Column(db.Integer, ForeignKey(PermScope.value), nullable=True)
[docs] scope = db.relationship(PermScope)
[docs] sensitivity_filter = db.Column(db.Boolean, server_default=sa.false(), nullable=False)
[docs] availability = db.relationship( PermissionAvailable, backref=db.backref("permissions", overlaps="action, object, module"), # overlaps expected overlaps="action, object, module", # overlaps expected )
[docs] filters_fields = { "SCOPE": scope_value, "SENSITIVITY": sensitivity_filter, }
@staticmethod
[docs] def __SCOPE_le__(a, b): return b is None or (a is not None and a <= b)
@staticmethod
[docs] def __SENSITIVITY_le__(a, b): # False only if: A is False and b is True return (not a) <= (not b)
@staticmethod
[docs] def __default_le__(a, b): return a == b or b is None
[docs] def __le__(self, other): """ Return True if this permission is supersed by 'other' permission. This requires all filters to be supersed by 'other' filters. """ for name, field in self.filters_fields.items(): # Get filter comparison function or use default comparison function __le_fct__ = getattr(self, f"__{name}_le__", Permission.__default_le__) self_value, other_value = getattr(self, field.name), getattr(other, field.name) if not __le_fct__(self_value, other_value): return False return True
@property
[docs] def filters(self): filters = [] for name, field in self.filters_fields.items(): value = getattr(self, field.name) if field.nullable: if value is None: continue if field.type.python_type == bool: if not value: continue filters.append(PermFilter(name, value)) return filters
[docs] def has_other_filters_than(self, *expected_filters): for flt in self.filters: if flt.name not in expected_filters: return True return False
@qfilter(query=True)
[docs] def nice_order(cls, **kwargs): return _nice_order(cls, kwargs["query"])