schema.py
brancholdstable
changeset 5993 50e1a6ad3e98
parent 5746 f4fc424747db
child 5752 b0bb553e3be4
equal deleted inserted replaced
5487:3ab2682a4b37 5993:50e1a6ad3e98
   172         mainvars.append('O')
   172         mainvars.append('O')
   173     if 'U' in defined:
   173     if 'U' in defined:
   174         mainvars.append('U')
   174         mainvars.append('U')
   175     if not mainvars:
   175     if not mainvars:
   176         raise Exception('unable to guess selection variables')
   176         raise Exception('unable to guess selection variables')
   177     return ','.join(mainvars)
   177     return ','.join(sorted(mainvars))
   178 
   178 
   179 def split_expression(rqlstring):
   179 def split_expression(rqlstring):
   180     for expr in rqlstring.split(','):
   180     for expr in rqlstring.split(','):
   181         for noparen in expr.split('('):
   181         for noparen in expr.split('('):
   182             for word in noparen.split():
   182             for word in noparen.split():
   469         else:
   469         else:
   470             assert not 'eid' in kwargs, kwargs
   470             assert not 'eid' in kwargs, kwargs
   471             assert action in ('read', 'add', 'delete')
   471             assert action in ('read', 'add', 'delete')
   472             if 'fromeid' in kwargs:
   472             if 'fromeid' in kwargs:
   473                 subjtype = session.describe(kwargs['fromeid'])[0]
   473                 subjtype = session.describe(kwargs['fromeid'])[0]
       
   474             elif 'frometype' in kwargs:
       
   475                 subjtype = kwargs.pop('frometype')
   474             else:
   476             else:
   475                 subjtype = None
   477                 subjtype = None
   476             if 'toeid' in kwargs:
   478             if 'toeid' in kwargs:
   477                 objtype = session.describe(kwargs['toeid'])[0]
   479                 objtype = session.describe(kwargs['toeid'])[0]
       
   480             elif 'toetype' in kwargs:
       
   481                 objtype = kwargs.pop('toetype')
   478             else:
   482             else:
   479                 objtype = None
   483                 objtype = None
   480         if objtype and subjtype:
   484         if objtype and subjtype:
   481             return self.rdef(subjtype, objtype).has_perm(session, action, **kwargs)
   485             return self.rdef(subjtype, objtype).has_perm(session, action, **kwargs)
   482         elif subjtype:
   486         elif subjtype:
   626 
   630 
   627     def serialize(self):
   631     def serialize(self):
   628         # start with a comma for bw compat, see below
   632         # start with a comma for bw compat, see below
   629         return ';' + self.mainvars + ';' + self.restriction
   633         return ';' + self.mainvars + ';' + self.restriction
   630 
   634 
       
   635     @classmethod
   631     def deserialize(cls, value):
   636     def deserialize(cls, value):
   632         # XXX < 3.5.10 bw compat
   637         # XXX < 3.5.10 bw compat
   633         if not value.startswith(';'):
   638         if not value.startswith(';'):
   634             return cls(value)
   639             return cls(value)
   635         _, mainvars, restriction = value.split(';', 2)
   640         _, mainvars, restriction = value.split(';', 2)
   636         return cls(restriction, mainvars)
   641         return cls(restriction, mainvars)
   637     deserialize = classmethod(deserialize)
       
   638 
   642 
   639     def check(self, entity, rtype, value):
   643     def check(self, entity, rtype, value):
   640         """return true if the value satisfy the constraint, else false"""
   644         """return true if the value satisfy the constraint, else false"""
   641         # implemented as a hook in the repository
   645         # implemented as a hook in the repository
   642         return 1
   646         return 1
   716 
   720 
   717     def exec_query(self, session, eidfrom, eidto):
   721     def exec_query(self, session, eidfrom, eidto):
   718         if eidto is None:
   722         if eidto is None:
   719             # checking constraint for an attribute relation
   723             # checking constraint for an attribute relation
   720             restriction = 'S eid %(s)s, ' + self.restriction
   724             restriction = 'S eid %(s)s, ' + self.restriction
   721             args, ck = {'s': eidfrom}, 's'
   725             args = {'s': eidfrom}
   722         else:
   726         else:
   723             restriction = 'S eid %(s)s, O eid %(o)s, ' + self.restriction
   727             restriction = 'S eid %(s)s, O eid %(o)s, ' + self.restriction
   724             args, ck = {'s': eidfrom, 'o': eidto}, ('s', 'o')
   728             args = {'s': eidfrom, 'o': eidto}
   725         rql = 'Any %s WHERE %s' % (self.mainvars,  restriction)
   729         rql = 'Any %s WHERE %s' % (self.mainvars,  restriction)
   726         if self.distinct_query:
   730         if self.distinct_query:
   727             rql = 'DISTINCT ' + rql
   731             rql = 'DISTINCT ' + rql
   728         return session.execute(rql, args, ck, build_descr=False)
   732         return session.execute(rql, args, build_descr=False)
   729 
   733 
   730 
   734 
   731 class RQLConstraint(RepoEnforcedRQLConstraintMixIn, RQLVocabularyConstraint):
   735 class RQLConstraint(RepoEnforcedRQLConstraintMixIn, RQLVocabularyConstraint):
   732     """the rql constraint is similar to the RQLVocabularyConstraint but
   736     """the rql constraint is similar to the RQLVocabularyConstraint but
   733     are also enforced at the repository level
   737     are also enforced at the repository level
   843             try:
   847             try:
   844                 return session.local_perm_cache[key]
   848                 return session.local_perm_cache[key]
   845             except KeyError:
   849             except KeyError:
   846                 pass
   850                 pass
   847         rql, has_perm_defs, keyarg = self.transform_has_permission()
   851         rql, has_perm_defs, keyarg = self.transform_has_permission()
   848         if creating:
   852         # when creating an entity, expression related to X satisfied
   849             # when creating an entity, consider has_*_permission satisfied
   853         if creating and 'X' in self.rqlst.defined_vars:
   850             if has_perm_defs:
   854             return True
   851                 return True
       
   852             return False
       
   853         if keyarg is None:
   855         if keyarg is None:
   854             kwargs.setdefault('u', session.user.eid)
   856             kwargs.setdefault('u', session.user.eid)
   855             cachekey = kwargs.keys()
       
   856             try:
   857             try:
   857                 rset = session.execute(rql, kwargs, cachekey, build_descr=True)
   858                 rset = session.execute(rql, kwargs, build_descr=True)
   858             except NotImplementedError:
   859             except NotImplementedError:
   859                 self.critical('cant check rql expression, unsupported rql %s', rql)
   860                 self.critical('cant check rql expression, unsupported rql %s', rql)
   860                 if self.eid is not None:
   861                 if self.eid is not None:
   861                     session.local_perm_cache[key] = False
   862                     session.local_perm_cache[key] = False
   862                 return False
   863                 return False
   863             except TypeResolverException, ex:
   864             except TypeResolverException, ex:
   864                 # some expression may not be resolvable with current kwargs
   865                 # some expression may not be resolvable with current kwargs
   865                 # (type conflict)
   866                 # (type conflict)
   866                 self.warning('%s: %s', rql, str(ex))
   867                 self.warning('%s: %s', rql, str(ex))
       
   868                 if self.eid is not None:
       
   869                     session.local_perm_cache[key] = False
       
   870                 return False
       
   871             except Unauthorized, ex:
       
   872                 self.debug('unauthorized %s: %s', rql, str(ex))
   867                 if self.eid is not None:
   873                 if self.eid is not None:
   868                     session.local_perm_cache[key] = False
   874                     session.local_perm_cache[key] = False
   869                 return False
   875                 return False
   870         else:
   876         else:
   871             rset = session.eid_rset(kwargs[keyarg])
   877             rset = session.eid_rset(kwargs[keyarg])
   983 
   989 
   984 from yams.buildobjs import _add_relation as yams_add_relation
   990 from yams.buildobjs import _add_relation as yams_add_relation
   985 
   991 
   986 class workflowable_definition(ybo.metadefinition):
   992 class workflowable_definition(ybo.metadefinition):
   987     """extends default EntityType's metaclass to add workflow relations
   993     """extends default EntityType's metaclass to add workflow relations
   988     (i.e. in_state and wf_info_for).
   994     (i.e. in_state, wf_info_for and custom_workflow). This is the default
   989     This is the default metaclass for WorkflowableEntityType
   995     metaclass for WorkflowableEntityType.
   990     """
   996     """
   991     def __new__(mcs, name, bases, classdict):
   997     def __new__(mcs, name, bases, classdict):
   992         abstract = classdict.pop('__abstract__', False)
   998         abstract = classdict.pop('__abstract__', False)
   993         cls = super(workflowable_definition, mcs).__new__(mcs, name, bases,
   999         cls = super(workflowable_definition, mcs).__new__(mcs, name, bases,
   994                                                           classdict)
  1000                                                           classdict)
   995         if not abstract:
  1001         if not abstract:
   996             make_workflowable(cls)
  1002             make_workflowable(cls)
   997         return cls
  1003         return cls
   998 
  1004 
       
  1005 class WorkflowableEntityType(ybo.EntityType):
       
  1006     """Use this base class instead of :class:`EntityType` to have workflow
       
  1007     relations (i.e. `in_state`, `wf_info_for` and `custom_workflow`) on your
       
  1008     entity type.
       
  1009     """
       
  1010     __metaclass__ = workflowable_definition
       
  1011     __abstract__ = True
       
  1012 
       
  1013 
   999 def make_workflowable(cls, in_state_descr=None):
  1014 def make_workflowable(cls, in_state_descr=None):
       
  1015     """Adds workflow relations as :class:`WorkflowableEntityType`, but usable on
       
  1016     existing classes which are not using that base class.
       
  1017     """
  1000     existing_rels = set(rdef.name for rdef in cls.__relations__)
  1018     existing_rels = set(rdef.name for rdef in cls.__relations__)
  1001     # let relation types defined in cw.schemas.workflow carrying
  1019     # let relation types defined in cw.schemas.workflow carrying
  1002     # cardinality, constraints and other relation definition properties
  1020     # cardinality, constraints and other relation definition properties
       
  1021     etype = getattr(cls, 'name', cls.__name__)
  1003     if 'custom_workflow' not in existing_rels:
  1022     if 'custom_workflow' not in existing_rels:
  1004         rdef = ybo.SubjectRelation('Workflow')
  1023         rdef = ybo.RelationDefinition(etype, 'custom_workflow', 'Workflow')
  1005         yams_add_relation(cls.__relations__, rdef, 'custom_workflow')
  1024         yams_add_relation(cls.__relations__, rdef)
  1006     if 'in_state' not in existing_rels:
  1025     if 'in_state' not in existing_rels:
  1007         rdef = ybo.SubjectRelation('State', description=in_state_descr)
  1026         rdef = ybo.RelationDefinition(etype, 'in_state', 'State',
  1008         yams_add_relation(cls.__relations__, rdef, 'in_state')
  1027                                       description=in_state_descr)
       
  1028         yams_add_relation(cls.__relations__, rdef)
  1009     if 'wf_info_for' not in existing_rels:
  1029     if 'wf_info_for' not in existing_rels:
  1010         rdef = ybo.ObjectRelation('TrInfo')
  1030         rdef = ybo.RelationDefinition('TrInfo', 'wf_info_for', etype)
  1011         yams_add_relation(cls.__relations__, rdef, 'wf_info_for')
  1031         yams_add_relation(cls.__relations__, rdef)
  1012 
       
  1013 class WorkflowableEntityType(ybo.EntityType):
       
  1014     __metaclass__ = workflowable_definition
       
  1015     __abstract__ = True
       
  1016 
  1032 
  1017 
  1033 
  1018 # schema loading ##############################################################
  1034 # schema loading ##############################################################
  1019 
  1035 
  1020 CONSTRAINTS['RQLConstraint'] = RQLConstraint
  1036 CONSTRAINTS['RQLConstraint'] = RQLConstraint