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 |