schema.py
branchstable
changeset 8743 27a83746aebd
parent 8695 358d8bed9626
child 8892 80783605d270
equal deleted inserted replaced
8742:bd374bd906f3 8743:27a83746aebd
   259                                  self.has_perm(req, 'read')):
   259                                  self.has_perm(req, 'read')):
   260         return False
   260         return False
   261     return self.has_local_role(action) or self.has_perm(req, action)
   261     return self.has_local_role(action) or self.has_perm(req, action)
   262 PermissionMixIn.may_have_permission = may_have_permission
   262 PermissionMixIn.may_have_permission = may_have_permission
   263 
   263 
   264 def has_perm(self, session, action, **kwargs):
   264 def has_perm(self, _cw, action, **kwargs):
   265     """return true if the action is granted globaly or localy"""
   265     """return true if the action is granted globaly or localy"""
   266     try:
   266     try:
   267         self.check_perm(session, action, **kwargs)
   267         self.check_perm(_cw, action, **kwargs)
   268         return True
   268         return True
   269     except Unauthorized:
   269     except Unauthorized:
   270         return False
   270         return False
   271 PermissionMixIn.has_perm = has_perm
   271 PermissionMixIn.has_perm = has_perm
   272 
   272 
   273 def check_perm(self, session, action, **kwargs):
   273 def check_perm(self, _cw, action, **kwargs):
   274     # NB: session may be a server session or a request object check user is
   274     # NB: _cw may be a server transaction or a request object.
   275     # in an allowed group, if so that's enough internal sessions should
   275     #
   276     # always stop there
   276     # check user is in an allowed group, if so that's enough internal
       
   277     # transactions should always stop there
   277     groups = self.get_groups(action)
   278     groups = self.get_groups(action)
   278     if session.user.matching_groups(groups):
   279     if _cw.user.matching_groups(groups):
   279         return
   280         return
   280     # if 'owners' in allowed groups, check if the user actually owns this
   281     # if 'owners' in allowed groups, check if the user actually owns this
   281     # object, if so that's enough
   282     # object, if so that's enough
       
   283     #
       
   284     # NB: give _cw to user.owns since user is not be bound to a transaction on
       
   285     # the repository side
   282     if 'owners' in groups and (
   286     if 'owners' in groups and (
   283           kwargs.get('creating')
   287           kwargs.get('creating')
   284           or ('eid' in kwargs and session.user.owns(kwargs['eid']))):
   288           or ('eid' in kwargs and _cw.user.owns(kwargs['eid']))):
   285         return
   289         return
   286     # else if there is some rql expressions, check them
   290     # else if there is some rql expressions, check them
   287     if any(rqlexpr.check(session, **kwargs)
   291     if any(rqlexpr.check(_cw, **kwargs)
   288            for rqlexpr in self.get_rqlexprs(action)):
   292            for rqlexpr in self.get_rqlexprs(action)):
   289         return
   293         return
   290     raise Unauthorized(action, str(self))
   294     raise Unauthorized(action, str(self))
   291 PermissionMixIn.check_perm = check_perm
   295 PermissionMixIn.check_perm = check_perm
   292 
   296 
   465             for rdef in self.rdefs.itervalues():
   469             for rdef in self.rdefs.itervalues():
   466                 if rdef.may_have_permission(action, req):
   470                 if rdef.may_have_permission(action, req):
   467                     return True
   471                     return True
   468         return False
   472         return False
   469 
   473 
   470     def has_perm(self, session, action, **kwargs):
   474     def has_perm(self, _cw, action, **kwargs):
   471         """return true if the action is granted globaly or localy"""
   475         """return true if the action is granted globaly or localy"""
   472         if self.final:
   476         if self.final:
   473             assert not ('fromeid' in kwargs or 'toeid' in kwargs), kwargs
   477             assert not ('fromeid' in kwargs or 'toeid' in kwargs), kwargs
   474             assert action in ('read', 'update')
   478             assert action in ('read', 'update')
   475             if 'eid' in kwargs:
   479             if 'eid' in kwargs:
   476                 subjtype = session.describe(kwargs['eid'])[0]
   480                 subjtype = _cw.describe(kwargs['eid'])[0]
   477             else:
   481             else:
   478                 subjtype = objtype = None
   482                 subjtype = objtype = None
   479         else:
   483         else:
   480             assert not 'eid' in kwargs, kwargs
   484             assert not 'eid' in kwargs, kwargs
   481             assert action in ('read', 'add', 'delete')
   485             assert action in ('read', 'add', 'delete')
   482             if 'fromeid' in kwargs:
   486             if 'fromeid' in kwargs:
   483                 subjtype = session.describe(kwargs['fromeid'])[0]
   487                 subjtype = _cw.describe(kwargs['fromeid'])[0]
   484             elif 'frometype' in kwargs:
   488             elif 'frometype' in kwargs:
   485                 subjtype = kwargs.pop('frometype')
   489                 subjtype = kwargs.pop('frometype')
   486             else:
   490             else:
   487                 subjtype = None
   491                 subjtype = None
   488             if 'toeid' in kwargs:
   492             if 'toeid' in kwargs:
   489                 objtype = session.describe(kwargs['toeid'])[0]
   493                 objtype = _cw.describe(kwargs['toeid'])[0]
   490             elif 'toetype' in kwargs:
   494             elif 'toetype' in kwargs:
   491                 objtype = kwargs.pop('toetype')
   495                 objtype = kwargs.pop('toetype')
   492             else:
   496             else:
   493                 objtype = None
   497                 objtype = None
   494         if objtype and subjtype:
   498         if objtype and subjtype:
   495             return self.rdef(subjtype, objtype).has_perm(session, action, **kwargs)
   499             return self.rdef(subjtype, objtype).has_perm(_cw, action, **kwargs)
   496         elif subjtype:
   500         elif subjtype:
   497             for tschema in self.targets(subjtype, 'subject'):
   501             for tschema in self.targets(subjtype, 'subject'):
   498                 rdef = self.rdef(subjtype, tschema)
   502                 rdef = self.rdef(subjtype, tschema)
   499                 if not rdef.has_perm(session, action, **kwargs):
   503                 if not rdef.has_perm(_cw, action, **kwargs):
   500                     return False
   504                     return False
   501         elif objtype:
   505         elif objtype:
   502             for tschema in self.targets(objtype, 'object'):
   506             for tschema in self.targets(objtype, 'object'):
   503                 rdef = self.rdef(tschema, objtype)
   507                 rdef = self.rdef(tschema, objtype)
   504                 if not rdef.has_perm(session, action, **kwargs):
   508                 if not rdef.has_perm(_cw, action, **kwargs):
   505                     return False
   509                     return False
   506         else:
   510         else:
   507             for rdef in self.rdefs.itervalues():
   511             for rdef in self.rdefs.itervalues():
   508                 if not rdef.has_perm(session, action, **kwargs):
   512                 if not rdef.has_perm(_cw, action, **kwargs):
   509                     return False
   513                     return False
   510         return True
   514         return True
   511 
   515 
   512     @deprecated('use .rdef(subjtype, objtype).role_cardinality(role)')
   516     @deprecated('use .rdef(subjtype, objtype).role_cardinality(role)')
   513     def cardinality(self, subjtype, objtype, target):
   517     def cardinality(self, subjtype, objtype, target):
   752                 keyarg = None
   756                 keyarg = None
   753             rqlst.recover()
   757             rqlst.recover()
   754             return rql, found, keyarg
   758             return rql, found, keyarg
   755         return rqlst.as_string(), None, None
   759         return rqlst.as_string(), None, None
   756 
   760 
   757     def _check(self, session, **kwargs):
   761     def _check(self, _cw, **kwargs):
   758         """return True if the rql expression is matching the given relation
   762         """return True if the rql expression is matching the given relation
   759         between fromeid and toeid
   763         between fromeid and toeid
   760 
   764 
   761         session may actually be a request as well
   765         _cw may be a request or a server side transaction
   762         """
   766         """
   763         creating = kwargs.get('creating')
   767         creating = kwargs.get('creating')
   764         if not creating and self.eid is not None:
   768         if not creating and self.eid is not None:
   765             key = (self.eid, tuple(sorted(kwargs.iteritems())))
   769             key = (self.eid, tuple(sorted(kwargs.iteritems())))
   766             try:
   770             try:
   767                 return session.local_perm_cache[key]
   771                 return _cw.local_perm_cache[key]
   768             except KeyError:
   772             except KeyError:
   769                 pass
   773                 pass
   770         rql, has_perm_defs, keyarg = self.transform_has_permission()
   774         rql, has_perm_defs, keyarg = self.transform_has_permission()
   771         # when creating an entity, expression related to X satisfied
   775         # when creating an entity, expression related to X satisfied
   772         if creating and 'X' in self.rqlst.defined_vars:
   776         if creating and 'X' in self.rqlst.defined_vars:
   773             return True
   777             return True
   774         if keyarg is None:
   778         if keyarg is None:
   775             kwargs.setdefault('u', session.user.eid)
   779             kwargs.setdefault('u', _cw.user.eid)
   776             try:
   780             try:
   777                 rset = session.execute(rql, kwargs, build_descr=True)
   781                 rset = _cw.execute(rql, kwargs, build_descr=True)
   778             except NotImplementedError:
   782             except NotImplementedError:
   779                 self.critical('cant check rql expression, unsupported rql %s', rql)
   783                 self.critical('cant check rql expression, unsupported rql %s', rql)
   780                 if self.eid is not None:
   784                 if self.eid is not None:
   781                     session.local_perm_cache[key] = False
   785                     _cw.local_perm_cache[key] = False
   782                 return False
   786                 return False
   783             except TypeResolverException, ex:
   787             except TypeResolverException as ex:
   784                 # some expression may not be resolvable with current kwargs
   788                 # some expression may not be resolvable with current kwargs
   785                 # (type conflict)
   789                 # (type conflict)
   786                 self.warning('%s: %s', rql, str(ex))
   790                 self.warning('%s: %s', rql, str(ex))
   787                 if self.eid is not None:
   791                 if self.eid is not None:
   788                     session.local_perm_cache[key] = False
   792                     _cw.local_perm_cache[key] = False
   789                 return False
   793                 return False
   790             except Unauthorized, ex:
   794             except Unauthorized as ex:
   791                 self.debug('unauthorized %s: %s', rql, str(ex))
   795                 self.debug('unauthorized %s: %s', rql, str(ex))
   792                 if self.eid is not None:
   796                 if self.eid is not None:
   793                     session.local_perm_cache[key] = False
   797                     _cw.local_perm_cache[key] = False
   794                 return False
   798                 return False
   795         else:
   799         else:
   796             rset = session.eid_rset(kwargs[keyarg])
   800             rset = _cw.eid_rset(kwargs[keyarg])
   797         # if no special has_*_permission relation in the rql expression, just
   801         # if no special has_*_permission relation in the rql expression, just
   798         # check the result set contains something
   802         # check the result set contains something
   799         if has_perm_defs is None:
   803         if has_perm_defs is None:
   800             if rset:
   804             if rset:
   801                 if self.eid is not None:
   805                 if self.eid is not None:
   802                     session.local_perm_cache[key] = True
   806                     _cw.local_perm_cache[key] = True
   803                 return True
   807                 return True
   804         elif rset:
   808         elif rset:
   805             # check every special has_*_permission relation is satisfied
   809             # check every special has_*_permission relation is satisfied
   806             get_eschema = session.vreg.schema.eschema
   810             get_eschema = _cw.vreg.schema.eschema
   807             try:
   811             try:
   808                 for eaction, col in has_perm_defs:
   812                 for eaction, col in has_perm_defs:
   809                     for i in xrange(len(rset)):
   813                     for i in xrange(len(rset)):
   810                         eschema = get_eschema(rset.description[i][col])
   814                         eschema = get_eschema(rset.description[i][col])
   811                         eschema.check_perm(session, eaction, eid=rset[i][col])
   815                         eschema.check_perm(_cw, eaction, eid=rset[i][col])
   812                 if self.eid is not None:
   816                 if self.eid is not None:
   813                     session.local_perm_cache[key] = True
   817                     _cw.local_perm_cache[key] = True
   814                 return True
   818                 return True
   815             except Unauthorized:
   819             except Unauthorized:
   816                 pass
   820                 pass
   817         if self.eid is not None:
   821         if self.eid is not None:
   818             session.local_perm_cache[key] = False
   822             _cw.local_perm_cache[key] = False
   819         return False
   823         return False
   820 
   824 
   821     @property
   825     @property
   822     def minimal_rql(self):
   826     def minimal_rql(self):
   823         return 'Any %s WHERE %s' % (','.join(sorted(self.mainvars)),
   827         return 'Any %s WHERE %s' % (','.join(sorted(self.mainvars)),
   841             rql += ', X eid %(x)s'
   845             rql += ', X eid %(x)s'
   842         if 'U' in defined:
   846         if 'U' in defined:
   843             rql += ', U eid %(u)s'
   847             rql += ', U eid %(u)s'
   844         return rql
   848         return rql
   845 
   849 
   846     def check(self, session, eid=None, creating=False, **kwargs):
   850     def check(self, _cw, eid=None, creating=False, **kwargs):
   847         if 'X' in self.rqlst.defined_vars:
   851         if 'X' in self.rqlst.defined_vars:
   848             if eid is None:
   852             if eid is None:
   849                 if creating:
   853                 if creating:
   850                     return self._check(session, creating=True, **kwargs)
   854                     return self._check(_cw, creating=True, **kwargs)
   851                 return False
   855                 return False
   852             assert creating == False
   856             assert creating == False
   853             return self._check(session, x=eid, **kwargs)
   857             return self._check(_cw, x=eid, **kwargs)
   854         return self._check(session, **kwargs)
   858         return self._check(_cw, **kwargs)
   855 
   859 
   856 
   860 
   857 def vargraph(rqlst):
   861 def vargraph(rqlst):
   858     """ builds an adjacency graph of variables from the rql syntax tree, e.g:
   862     """ builds an adjacency graph of variables from the rql syntax tree, e.g:
   859     Any O,S WHERE T subworkflow_exit S, T subworkflow WF, O state_of WF
   863     Any O,S WHERE T subworkflow_exit S, T subworkflow WF, O state_of WF
   902             rql += ', O eid %(o)s'
   906             rql += ', O eid %(o)s'
   903         if 'U' in defined:
   907         if 'U' in defined:
   904             rql += ', U eid %(u)s'
   908             rql += ', U eid %(u)s'
   905         return rql
   909         return rql
   906 
   910 
   907     def check(self, session, fromeid=None, toeid=None):
   911     def check(self, _cw, fromeid=None, toeid=None):
   908         kwargs = {}
   912         kwargs = {}
   909         if 'S' in self.rqlst.defined_vars:
   913         if 'S' in self.rqlst.defined_vars:
   910             if fromeid is None:
   914             if fromeid is None:
   911                 return False
   915                 return False
   912             kwargs['s'] = fromeid
   916             kwargs['s'] = fromeid
   913         if 'O' in self.rqlst.defined_vars:
   917         if 'O' in self.rqlst.defined_vars:
   914             if toeid is None:
   918             if toeid is None:
   915                 return False
   919                 return False
   916             kwargs['o'] = toeid
   920             kwargs['o'] = toeid
   917         return self._check(session, **kwargs)
   921         return self._check(_cw, **kwargs)
   918 
   922 
   919 
   923 
   920 # in yams, default 'update' perm for attributes granted to managers and owners.
   924 # in yams, default 'update' perm for attributes granted to managers and owners.
   921 # Within cw, we want to default to users who may edit the entity holding the
   925 # Within cw, we want to default to users who may edit the entity holding the
   922 # attribute.
   926 # attribute.
  1022                 msg = '%(constraint)s %(expression)s failed' % {
  1026                 msg = '%(constraint)s %(expression)s failed' % {
  1023                     'constraint':  session._(self.type()),
  1027                     'constraint':  session._(self.type()),
  1024                     'expression': self.expression}
  1028                     'expression': self.expression}
  1025             raise ValidationError(maineid, {qname: msg})
  1029             raise ValidationError(maineid, {qname: msg})
  1026 
  1030 
  1027     def exec_query(self, session, eidfrom, eidto):
  1031     def exec_query(self, _cw, eidfrom, eidto):
  1028         if eidto is None:
  1032         if eidto is None:
  1029             # checking constraint for an attribute relation
  1033             # checking constraint for an attribute relation
  1030             expression = 'S eid %(s)s, ' + self.expression
  1034             expression = 'S eid %(s)s, ' + self.expression
  1031             args = {'s': eidfrom}
  1035             args = {'s': eidfrom}
  1032         else:
  1036         else:
  1033             expression = 'S eid %(s)s, O eid %(o)s, ' + self.expression
  1037             expression = 'S eid %(s)s, O eid %(o)s, ' + self.expression
  1034             args = {'s': eidfrom, 'o': eidto}
  1038             args = {'s': eidfrom, 'o': eidto}
  1035         if 'U' in self.rqlst.defined_vars:
  1039         if 'U' in self.rqlst.defined_vars:
  1036             expression = 'U eid %(u)s, ' + expression
  1040             expression = 'U eid %(u)s, ' + expression
  1037             args['u'] = session.user.eid
  1041             args['u'] = _cw.user.eid
  1038         rql = 'Any %s WHERE %s' % (','.join(sorted(self.mainvars)), expression)
  1042         rql = 'Any %s WHERE %s' % (','.join(sorted(self.mainvars)), expression)
  1039         if self.distinct_query:
  1043         if self.distinct_query:
  1040             rql = 'DISTINCT ' + rql
  1044             rql = 'DISTINCT ' + rql
  1041         return session.execute(rql, args, build_descr=False)
  1045         return _cw.execute(rql, args, build_descr=False)
  1042 
  1046 
  1043 
  1047 
  1044 class RQLConstraint(RepoEnforcedRQLConstraintMixIn, RQLVocabularyConstraint):
  1048 class RQLConstraint(RepoEnforcedRQLConstraintMixIn, RQLVocabularyConstraint):
  1045     """the rql constraint is similar to the RQLVocabularyConstraint but
  1049     """the rql constraint is similar to the RQLVocabularyConstraint but
  1046     are also enforced at the repository level
  1050     are also enforced at the repository level
  1059     is no way to guess it correctly (e.g. if using S,O or U the constraint will
  1063     is no way to guess it correctly (e.g. if using S,O or U the constraint will
  1060     always be satisfied because we've to use a DISTINCT query).
  1064     always be satisfied because we've to use a DISTINCT query).
  1061     """
  1065     """
  1062     # XXX turns mainvars into a required argument in __init__
  1066     # XXX turns mainvars into a required argument in __init__
  1063     distinct_query = True
  1067     distinct_query = True
  1064 
  1068  
  1065     def match_condition(self, session, eidfrom, eidto):
  1069     def match_condition(self, session, eidfrom, eidto):
  1066         return len(self.exec_query(session, eidfrom, eidto)) <= 1
  1070         return len(self.exec_query(session, eidfrom, eidto)) <= 1
  1067 
  1071 
  1068 
  1072 
  1069 # workflow extensions #########################################################
  1073 # workflow extensions #########################################################