class Test: id = Integer(primary_key=True) properties = Json() name = JsonRelated(json_column='properties', keys=['name'], set_adapter=str, get_adapter=int)
class Test: id = Integer(primary_key=True) properties = Json() name = JsonRelated(json_column='properties', keys=['name'], set_adapter='fromint', get_adapter='toint') def fromint(self, value): if value: return str(value) return value def toint(self, value): if value: return int(value) return value
class Test: id = Integer(primary_key=True) properties = Json() name = JsonRelated(json_column='properties')
class Test: id = Integer(primary_key=True) properties = Json() name = JsonRelated(keys=['name'])
class Authorization: """A model to store autorization rules (permissions for users against an Anyblok model or a Pyramid resource)""" id = Integer(primary_key=True) order = Integer(default=100, nullable=False) resource = String() model = String( foreign_key=Declarations.Model.System.Model.use('name').options( ondelete="cascade")) primary_keys = Json(default={}) filter = Json(default={}) # next step role = Many2One(model=User.Role, foreign_key_options={'ondelete': 'cascade'}) login = String(foreign_key=User.use('login').options(ondelete="cascade")) user = Many2One(model=User) perms = Json(default={}) perm_create = JsonRelated(json_column='perms', keys=['create']) perm_read = JsonRelated(json_column='perms', keys=['read']) perm_update = JsonRelated(json_column='perms', keys=['update']) perm_delete = JsonRelated(json_column='perms', keys=['delete']) @classmethod def get_acl_filter_model(cls): """Return the Model to use to check the permission""" return { 'User': cls.registry.User, 'Role': cls.registry.User.Role, } @classmethod def get_acl(cls, login, resource, params=None): """Return the Pyramid ACL in function of the resource and user :param login: str, login of the user :param resource: str, name of the resource """ # cache the method User = cls.registry.User Role = cls.registry.User.Role query = cls.query() query = query.filter( or_(cls.resource == resource, cls.model == resource)) query = query.order_by(cls.order) Q1 = query.filter(cls.login == login) Q2 = query.join(cls.role).filter(Role.name.in_(User.get_roles(login))) res = [] for query in (Q1, Q2): for self in query.all(): allow_perms = [] deny_perms = [] perms = list((self.perms or {}).keys()) perms.sort() for perm in perms: p = self.perms[perm] query = User.query() query = query.filter(User.login == login) query = query.join(User.roles) if self.filter: query = query.condition_filter( self.filter, cls.get_acl_filter_model()) if 'condition' in p: query = query.condition_filter( p['condition'], cls.get_acl_filter_model()) ismatched = True if query.count() else False if p.get('matched' if ismatched else 'unmatched') is True: allow_perms.append(perm) elif (p.get('matched' if ismatched else 'unmatched') is False): deny_perms.append(perm) if len(allow_perms): res.append((Allow, login, allow_perms)) if len(deny_perms): res.append((Deny, login, deny_perms)) res.append((Deny, login, ALL_PERMISSIONS)) return res @classmethod def before_insert_orm_event(cls, mapper, connection, target): target.check_validity() @classmethod def before_update_orm_event(cls, mapper, connection, target): target.check_validity() def check_validity(self): """When creating or updating a User.Authorization, check that all rules objects exists or return an AuthorizationValidationException :exception: AuthorizationValidationException """ if not (self.role or self.login or self.user or self.role_name): raise AuthorizationValidationException( "No role and login to apply in the authorization (%s)" % self) if not (self.resource or self.model): raise AuthorizationValidationException( "No resource and model to apply in the authorization (%s)" % self) if not self.model and self.primary_keys: raise AuthorizationValidationException( "Primary keys without model to apply in the authorization " "(%s)" % self)
class Authorization: """A model to store autorization rules (permissions for users against an Anyblok model or a Pyramid resource)""" id = Integer(primary_key=True) code = String(nullable=True, unique=True, size=256) order = Integer(default=100, nullable=False) resource = String() model = String( foreign_key=Declarations.Model.System.Model.use('name').options( ondelete="cascade"), size=256, ) primary_keys = Json(default={}) filter = Json(default={}) # next step role = Many2One( model=Pyramid.Role, foreign_key_options={'ondelete': 'cascade'}) login = String( foreign_key=Pyramid.User.use('login').options(ondelete="cascade")) user = Many2One(model=Pyramid.User) perms = Json(default={}) perm_create = JsonRelated(json_column='perms', keys=['create']) perm_read = JsonRelated(json_column='perms', keys=['read']) perm_update = JsonRelated(json_column='perms', keys=['update']) perm_delete = JsonRelated(json_column='perms', keys=['delete']) @classmethod def get_acl_filter_model(cls): """Return the Model to use to check the permission""" return { 'User': cls.anyblok.Pyramid.User, 'Role': cls.anyblok.Pyramid.Role, } @classmethod def get_acl(cls, login, resource, params=None): """Return the Pyramid ACL in function of the resource and user :param login: str, login of the user :param resource: str, name of the resource """ # cache the method User = cls.anyblok.Pyramid.User Role = cls.anyblok.Pyramid.Role query = cls.query() query = query.filter( or_(cls.resource == resource, cls.model == resource)) query = query.order_by(cls.order) Q1 = query.filter(cls.login == login) Q2 = query.join(cls.role).filter(Role.name.in_(User.get_roles(login))) res = [] for query in (Q1, Q2): for self in query.all(): allow_perms = [] deny_perms = [] perms = list((self.perms or {}).keys()) perms.sort() for perm in perms: p = self.perms[perm] query = User.query() query = query.filter(User.login == login) query = query.join(User.roles) if self.filter: query = query.condition_filter( self.filter, cls.get_acl_filter_model() ) if 'condition' in p: query = query.condition_filter( p['condition'], cls.get_acl_filter_model() ) ismatched = True if query.count() else False if p.get('matched' if ismatched else 'unmatched') is True: allow_perms.append(perm) elif ( p.get('matched' if ismatched else 'unmatched') is False ): deny_perms.append(perm) if len(allow_perms): res.append((Allow, login, allow_perms)) if len(deny_perms): res.append((Deny, login, deny_perms)) res.append((Deny, login, ALL_PERMISSIONS)) return res @classmethod def check_acl(cls, login, resource, type_): """Return the Pyramid ACL in function of the resource and user :param login: str, login of the user :param resource: str, name of the resource :param type: str, name of the action """ # cache the method User = cls.anyblok.Pyramid.User Role = cls.anyblok.Pyramid.Role query = cls.query() query = query.filter( or_(cls.resource == resource, cls.model == resource)) query = query.order_by(cls.order) Q1 = query.filter(cls.login == login) Q2 = query.join(cls.role).filter(Role.name.in_(User.get_roles(login))) for query in (Q1, Q2): for self in query.all(): perms = list((self.perms or {}).keys()) if type_ not in perms: continue p = self.perms[type_] query = User.query() query = query.filter(User.login == login) query = query.join(User.roles, isouter=True) if self.filter: query = query.condition_filter( self.filter, cls.get_acl_filter_model() ) if 'condition' in p: query = query.condition_filter( p['condition'], cls.get_acl_filter_model() ) ismatched = True if query.count() else False if p.get('matched' if ismatched else 'unmatched') is True: return True elif ( p.get('matched' if ismatched else 'unmatched') is False ): return False return False @classmethod def before_insert_orm_event(cls, mapper, connection, target): target.check_validity() @classmethod def before_update_orm_event(cls, mapper, connection, target): target.check_validity() def check_validity(self): """When creating or updating a User.Authorization, check that all rules objects exists or return an AuthorizationValidationException :exception: AuthorizationValidationException """ if not (self.role or self.login or self.user or self.role_name): raise AuthorizationValidationException( "No role and login to apply in the authorization (%s)" % self) if not (self.resource or self.model): raise AuthorizationValidationException( "No resource and model to apply in the authorization (%s)" % self) if not self.model and self.primary_keys: raise AuthorizationValidationException( "Primary keys without model to apply in the authorization " "(%s)" % self) @classmethod def ensure_exists( cls, code, **kwargs ): """Ensure role's authorization is present :param code: String, authorization code. :param kwargs: authorization fields """ if not kwargs: kwargs = {} # pragma: no cover # pv: at some point adding index on this criteria may boost things # while setting authorizations authz = ( cls.anyblok.Pyramid.Authorization.query() .filter_by( code=code, ) .one_or_none() ) if not authz: authz = cls.anyblok.Pyramid.Authorization.insert( code=code, **kwargs ) else: authz.update(**kwargs) jsonfields = {"perms", "primary_keys", "filter"} perms_related = { "perm_create", "perm_read", "perm_update", "perm_delete" } modified = (jsonfields | perms_related) & set(kwargs.keys()) if modified: if perms_related & modified: modified = modified | {"perms"} authz.flag_modified(*(jsonfields & modified)) return authz