schema.py
branchreldefsecurity
changeset 3877 7ca53fc72a0a
parent 3827 c7142a4e3470
child 3890 d7a270f50f54
equal deleted inserted replaced
3876:1169d3154be6 3877:7ca53fc72a0a
    18 from logilab.common.deprecation import deprecated
    18 from logilab.common.deprecation import deprecated
    19 from logilab.common.graph import get_cycles
    19 from logilab.common.graph import get_cycles
    20 from logilab.common.compat import any
    20 from logilab.common.compat import any
    21 
    21 
    22 from yams import BadSchemaDefinition, buildobjs as ybo
    22 from yams import BadSchemaDefinition, buildobjs as ybo
    23 from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema
    23 from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema, \
       
    24      RelationDefinitionSchema, PermissionMixIn
    24 from yams.constraints import (BaseConstraint, StaticVocabularyConstraint,
    25 from yams.constraints import (BaseConstraint, StaticVocabularyConstraint,
    25                               FormatConstraint)
    26                               FormatConstraint)
    26 from yams.reader import (CONSTRAINTS, PyFileReader, SchemaLoader,
    27 from yams.reader import (CONSTRAINTS, PyFileReader, SchemaLoader,
    27                          obsolete as yobsolete, cleanup_sys_modules)
    28                          obsolete as yobsolete, cleanup_sys_modules)
    28 
    29 
   125     """
   126     """
   126     return display_name(req, self.type, form)
   127     return display_name(req, self.type, form)
   127 ERSchema.display_name = ERSchema_display_name
   128 ERSchema.display_name = ERSchema_display_name
   128 
   129 
   129 @cached
   130 @cached
   130 def ERSchema_get_groups(self, action):
   131 def get_groups(self, action):
   131     """return the groups authorized to perform <action> on entities of
   132     """return the groups authorized to perform <action> on entities of
   132     this type
   133     this type
   133 
   134 
   134     :type action: str
   135     :type action: str
   135     :param action: the name of a permission
   136     :param action: the name of a permission
   138     :return: names of the groups with the given permission
   139     :return: names of the groups with the given permission
   139     """
   140     """
   140     assert action in self.ACTIONS, action
   141     assert action in self.ACTIONS, action
   141     #assert action in self._groups, '%s %s' % (self, action)
   142     #assert action in self._groups, '%s %s' % (self, action)
   142     try:
   143     try:
   143         return frozenset(g for g in self._groups[action] if isinstance(g, basestring))
   144         return frozenset(g for g in self.permissions[action] if isinstance(g, basestring))
   144     except KeyError:
   145     except KeyError:
   145         return ()
   146         return ()
   146 ERSchema.get_groups = ERSchema_get_groups
   147 PermissionMixIn.get_groups = get_groups
   147 
       
   148 def ERSchema_set_groups(self, action, groups):
       
   149     """set the groups allowed to perform <action> on entities of this type. Don't
       
   150     change rql expressions for the same action.
       
   151 
       
   152     :type action: str
       
   153     :param action: the name of a permission
       
   154 
       
   155     :type groups: list or tuple
       
   156     :param groups: names of the groups granted to do the given action
       
   157     """
       
   158     assert action in self.ACTIONS, action
       
   159     clear_cache(self, 'ERSchema_get_groups')
       
   160     self._groups[action] = tuple(groups) + self.get_rqlexprs(action)
       
   161 ERSchema.set_groups = ERSchema_set_groups
       
   162 
   148 
   163 @cached
   149 @cached
   164 def ERSchema_get_rqlexprs(self, action):
   150 def get_rqlexprs(self, action):
   165     """return the rql expressions representing queries to check the user is allowed
   151     """return the rql expressions representing queries to check the user is allowed
   166     to perform <action> on entities of this type
   152     to perform <action> on entities of this type
   167 
   153 
   168     :type action: str
   154     :type action: str
   169     :param action: the name of a permission
   155     :param action: the name of a permission
   172     :return: the rql expressions with the given permission
   158     :return: the rql expressions with the given permission
   173     """
   159     """
   174     assert action in self.ACTIONS, action
   160     assert action in self.ACTIONS, action
   175     #assert action in self._rqlexprs, '%s %s' % (self, action)
   161     #assert action in self._rqlexprs, '%s %s' % (self, action)
   176     try:
   162     try:
   177         return tuple(g for g in self._groups[action] if not isinstance(g, basestring))
   163         return tuple(g for g in self.permissions[action] if not isinstance(g, basestring))
   178     except KeyError:
   164     except KeyError:
   179         return ()
   165         return ()
   180 ERSchema.get_rqlexprs = ERSchema_get_rqlexprs
   166 PermissionMixIn.get_rqlexprs = get_rqlexprs
   181 
   167 
   182 def ERSchema_set_rqlexprs(self, action, rqlexprs):
   168 orig_set_action_permissions = PermissionMixIn.set_action_permissions
   183     """set the rql expression allowing to perform <action> on entities of this type. Don't
   169 def set_action_permissions(self, action, permissions):
   184     change groups for the same action.
   170     """set the groups and rql expressions allowing to perform <action> on
       
   171     entities of this type
   185 
   172 
   186     :type action: str
   173     :type action: str
   187     :param action: the name of a permission
   174     :param action: the name of a permission
   188 
   175 
   189     :type rqlexprs: list or tuple
       
   190     :param rqlexprs: the rql expressions allowing the given action
       
   191     """
       
   192     assert action in self.ACTIONS, action
       
   193     clear_cache(self, 'ERSchema_get_rqlexprs')
       
   194     self._groups[action] = tuple(self.get_groups(action)) + tuple(rqlexprs)
       
   195 ERSchema.set_rqlexprs = ERSchema_set_rqlexprs
       
   196 
       
   197 def ERSchema_set_permissions(self, action, permissions):
       
   198     """set the groups and rql expressions allowing to perform <action> on
       
   199     entities of this type
       
   200 
       
   201     :type action: str
       
   202     :param action: the name of a permission
       
   203 
       
   204     :type permissions: tuple
   176     :type permissions: tuple
   205     :param permissions: the groups and rql expressions allowing the given action
   177     :param permissions: the groups and rql expressions allowing the given action
   206     """
   178     """
   207     assert action in self.ACTIONS, action
   179     orig_set_action_permissions(self, action, tuple(permissions))
   208     clear_cache(self, 'ERSchema_get_rqlexprs')
   180     clear_cache(self, 'get_rqlexprs')
   209     clear_cache(self, 'ERSchema_get_groups')
   181     clear_cache(self, 'get_groups')
   210     self._groups[action] = tuple(permissions)
   182 PermissionMixIn.set_action_permissions = set_action_permissions
   211 ERSchema.set_permissions = ERSchema_set_permissions
   183 
   212 
   184 def has_local_role(self, action):
   213 def ERSchema_has_perm(self, session, action, *args, **kwargs):
       
   214     """return true if the action is granted globaly or localy"""
       
   215     try:
       
   216         self.check_perm(session, action, *args, **kwargs)
       
   217         return True
       
   218     except Unauthorized:
       
   219         return False
       
   220 ERSchema.has_perm = ERSchema_has_perm
       
   221 
       
   222 def ERSchema_has_local_role(self, action):
       
   223     """return true if the action *may* be granted localy (eg either rql
   185     """return true if the action *may* be granted localy (eg either rql
   224     expressions or the owners group are used in security definition)
   186     expressions or the owners group are used in security definition)
   225 
   187 
   226     XXX this method is only there since we don't know well how to deal with
   188     XXX this method is only there since we don't know well how to deal with
   227     'add' action checking. Also find a better name would be nice.
   189     'add' action checking. Also find a better name would be nice.
   228     """
   190     """
   229     assert action in self.ACTIONS, action
   191     assert action in self.ACTIONS, action
   230     if self.get_rqlexprs(action):
   192     if self.get_rqlexprs(action):
   231         return True
   193         return True
   232     if action in ('update', 'delete'):
   194     if action in ('update', 'delete'):
   233         return self.has_group(action, 'owners')
   195         return 'owners' in self.get_groups(action)
   234     return False
   196     return False
   235 ERSchema.has_local_role = ERSchema_has_local_role
   197 PermissionMixIn.has_local_role = has_local_role
       
   198 
       
   199 def may_have_permission(self, action, req):
       
   200     if action != 'read' and not (self.has_local_role('read') or
       
   201                                  self.has_perm(req, 'read')):
       
   202         return False
       
   203     return self.has_local_role(action) or self.has_perm(req, action)
       
   204 PermissionMixIn.may_have_permission = may_have_permission
       
   205 
       
   206 def has_perm(self, session, action, **kwargs):
       
   207     """return true if the action is granted globaly or localy"""
       
   208     try:
       
   209         self.check_perm(session, action, **kwargs)
       
   210         return True
       
   211     except Unauthorized:
       
   212         return False
       
   213 PermissionMixIn.has_perm = has_perm
       
   214 
       
   215 def check_perm(self, session, action, **kwargs):
       
   216     # NB: session may be a server session or a request object check user is
       
   217     # in an allowed group, if so that's enough internal sessions should
       
   218     # always stop there
       
   219     groups = self.get_groups(action)
       
   220     if session.user.matching_groups(groups):
       
   221         return
       
   222     # if 'owners' in allowed groups, check if the user actually owns this
       
   223     # object, if so that's enough
       
   224     if 'owners' in groups and 'eid' in kwargs and session.user.owns(kwargs['eid']):
       
   225         return
       
   226     # else if there is some rql expressions, check them
       
   227     if any(rqlexpr.check(session, **kwargs)
       
   228            for rqlexpr in self.get_rqlexprs(action)):
       
   229         return
       
   230     raise Unauthorized(action, str(self))
       
   231 PermissionMixIn.check_perm = check_perm
       
   232 
       
   233 
       
   234 RelationDefinitionSchema._RPROPERTIES['eid'] = None
       
   235 
       
   236 def rql_expression(self, expression, mainvars=None, eid=None):
       
   237     """rql expression factory"""
       
   238     if self.rtype.final:
       
   239         return ERQLExpression(expression, mainvars, eid)
       
   240     return RRQLExpression(expression, mainvars, eid)
       
   241 RelationDefinitionSchema.rql_expression = rql_expression
       
   242 
       
   243 orig_check_permission_definitions = RelationDefinitionSchema.check_permission_definitions
       
   244 def check_permission_definitions(self):
       
   245     orig_check_permission_definitions(self)
       
   246     schema = self.subject.schema
       
   247     for action, groups in self.permissions.iteritems():
       
   248         for group_or_rqlexpr in groups:
       
   249             if action == 'read' and \
       
   250                    isinstance(group_or_rqlexpr, RQLExpression):
       
   251                 msg = "can't use rql expression for read permission of %s"
       
   252                 raise BadSchemaDefinition(msg % self)
       
   253             elif self.final and isinstance(group_or_rqlexpr, RRQLExpression):
       
   254                 if schema.reading_from_database:
       
   255                     # we didn't have final relation earlier, so turn
       
   256                     # RRQLExpression into ERQLExpression now
       
   257                     rqlexpr = group_or_rqlexpr
       
   258                     newrqlexprs = [x for x in self.get_rqlexprs(action)
       
   259                                    if not x is rqlexpr]
       
   260                     newrqlexprs.append(ERQLExpression(rqlexpr.expression,
       
   261                                                       rqlexpr.mainvars,
       
   262                                                       rqlexpr.eid))
       
   263                     self.set_rqlexprs(action, newrqlexprs)
       
   264                 else:
       
   265                     msg = "can't use RRQLExpression on %s, use an ERQLExpression"
       
   266                     raise BadSchemaDefinition(msg % self)
       
   267             elif not self.final and \
       
   268                      isinstance(group_or_rqlexpr, ERQLExpression):
       
   269                 msg = "can't use ERQLExpression on %s, use a RRQLExpression"
       
   270                 raise BadSchemaDefinition(msg % self)
       
   271 RelationDefinitionSchema.check_permission_definitions = check_permission_definitions
   236 
   272 
   237 
   273 
   238 def system_etypes(schema):
   274 def system_etypes(schema):
   239     """return system entity types only: skip final, schema and application entities
   275     """return system entity types only: skip final, schema and application entities
   240     """
   276     """
   254         super(CubicWebEntitySchema, self).__init__(schema, edef, **kwargs)
   290         super(CubicWebEntitySchema, self).__init__(schema, edef, **kwargs)
   255         if eid is None and edef is not None:
   291         if eid is None and edef is not None:
   256             eid = getattr(edef, 'eid', None)
   292             eid = getattr(edef, 'eid', None)
   257         self.eid = eid
   293         self.eid = eid
   258         # take care: no _groups attribute when deep-copying
   294         # take care: no _groups attribute when deep-copying
   259         if getattr(self, '_groups', None):
   295         if getattr(self, 'permissions', None):
   260             for groups in self._groups.itervalues():
   296             for groups in self.permissions.itervalues():
   261                 for group_or_rqlexpr in groups:
   297                 for group_or_rqlexpr in groups:
   262                     if isinstance(group_or_rqlexpr, RRQLExpression):
   298                     if isinstance(group_or_rqlexpr, RRQLExpression):
   263                         msg = "can't use RRQLExpression on an entity type, use an ERQLExpression (%s)"
   299                         msg = "can't use RRQLExpression on an entity type, use an ERQLExpression (%s)"
   264                         raise BadSchemaDefinition(msg % self.type)
   300                         raise BadSchemaDefinition(msg % self.type)
   265 
   301 
   302         need_has_text = None
   338         need_has_text = None
   303         for rschema in self.subject_relations():
   339         for rschema in self.subject_relations():
   304             if rschema.final:
   340             if rschema.final:
   305                 if rschema == 'has_text':
   341                 if rschema == 'has_text':
   306                     has_has_text = True
   342                     has_has_text = True
   307                 elif self.rproperty(rschema, 'fulltextindexed'):
   343                 elif self.rdef(rschema).get('fulltextindexed'):
   308                     may_need_has_text = True
   344                     may_need_has_text = True
   309             elif rschema.fulltext_container:
   345             elif rschema.fulltext_container:
   310                 if rschema.fulltext_container == 'subject':
   346                 if rschema.fulltext_container == 'subject':
   311                     may_need_has_text = True
   347                     may_need_has_text = True
   312                 else:
   348                 else:
   327 
   363 
   328     def schema_entity(self):
   364     def schema_entity(self):
   329         """return True if this entity type is used to build the schema"""
   365         """return True if this entity type is used to build the schema"""
   330         return self.type in SCHEMA_TYPES
   366         return self.type in SCHEMA_TYPES
   331 
   367 
   332     def check_perm(self, session, action, eid=None):
       
   333         # NB: session may be a server session or a request object
       
   334         user = session.user
       
   335         # check user is in an allowed group, if so that's enough
       
   336         # internal sessions should always stop there
       
   337         if user.matching_groups(self.get_groups(action)):
       
   338             return
       
   339         # if 'owners' in allowed groups, check if the user actually owns this
       
   340         # object, if so that's enough
       
   341         if eid is not None and 'owners' in self.get_groups(action) and \
       
   342                user.owns(eid):
       
   343             return
       
   344         # else if there is some rql expressions, check them
       
   345         if any(rqlexpr.check(session, eid)
       
   346                for rqlexpr in self.get_rqlexprs(action)):
       
   347             return
       
   348         raise Unauthorized(action, str(self))
       
   349 
       
   350     def rql_expression(self, expression, mainvars=None, eid=None):
   368     def rql_expression(self, expression, mainvars=None, eid=None):
   351         """rql expression factory"""
   369         """rql expression factory"""
   352         return ERQLExpression(expression, mainvars, eid)
   370         return ERQLExpression(expression, mainvars, eid)
   353 
   371 
   354 
   372 
   355 class CubicWebRelationSchema(RelationSchema):
   373 class CubicWebRelationSchema(RelationSchema):
   356     RelationSchema._RPROPERTIES['eid'] = None
       
   357     _perms_checked = False
       
   358 
   374 
   359     def __init__(self, schema=None, rdef=None, eid=None, **kwargs):
   375     def __init__(self, schema=None, rdef=None, eid=None, **kwargs):
   360         if rdef is not None:
   376         if rdef is not None:
   361             # if this relation is inlined
   377             # if this relation is inlined
   362             self.inlined = rdef.inlined
   378             self.inlined = rdef.inlined
   367 
   383 
   368     @property
   384     @property
   369     def meta(self):
   385     def meta(self):
   370         return self.type in META_RTYPES
   386         return self.type in META_RTYPES
   371 
   387 
   372     def update(self, subjschema, objschema, rdef):
       
   373         super(CubicWebRelationSchema, self).update(subjschema, objschema, rdef)
       
   374         if not self._perms_checked and self._groups:
       
   375             for action, groups in self._groups.iteritems():
       
   376                 for group_or_rqlexpr in groups:
       
   377                     if action == 'read' and \
       
   378                            isinstance(group_or_rqlexpr, RQLExpression):
       
   379                         msg = "can't use rql expression for read permission of "\
       
   380                               "a relation type (%s)"
       
   381                         raise BadSchemaDefinition(msg % self.type)
       
   382                     elif self.final and isinstance(group_or_rqlexpr, RRQLExpression):
       
   383                         if self.schema.reading_from_database:
       
   384                             # we didn't have final relation earlier, so turn
       
   385                             # RRQLExpression into ERQLExpression now
       
   386                             rqlexpr = group_or_rqlexpr
       
   387                             newrqlexprs = [x for x in self.get_rqlexprs(action) if not x is rqlexpr]
       
   388                             newrqlexprs.append(ERQLExpression(rqlexpr.expression,
       
   389                                                               rqlexpr.mainvars,
       
   390                                                               rqlexpr.eid))
       
   391                             self.set_rqlexprs(action, newrqlexprs)
       
   392                         else:
       
   393                             msg = "can't use RRQLExpression on a final relation "\
       
   394                                   "type (eg attribute relation), use an ERQLExpression (%s)"
       
   395                             raise BadSchemaDefinition(msg % self.type)
       
   396                     elif not self.final and \
       
   397                              isinstance(group_or_rqlexpr, ERQLExpression):
       
   398                         msg = "can't use ERQLExpression on a relation type, use "\
       
   399                               "a RRQLExpression (%s)"
       
   400                         raise BadSchemaDefinition(msg % self.type)
       
   401             self._perms_checked = True
       
   402 
       
   403     def cardinality(self, subjtype, objtype, target):
       
   404         card = self.rproperty(subjtype, objtype, 'cardinality')
       
   405         return (target == 'subject' and card[0]) or \
       
   406                (target == 'object' and card[1])
       
   407 
       
   408     def schema_relation(self):
   388     def schema_relation(self):
   409         """return True if this relation type is used to build the schema"""
   389         """return True if this relation type is used to build the schema"""
   410         return self.type in SCHEMA_TYPES
   390         return self.type in SCHEMA_TYPES
   411 
   391 
   412     def physical_mode(self):
   392     def may_have_permission(self, action, req, eschema=None, role=None):
   413         """return an appropriate mode for physical storage of this relation type:
   393         if eschema is not None:
   414         * 'subjectinline' if every possible subject cardinalities are 1 or ?
   394             for tschema in rschema.targets(eschema, role):
   415         * 'objectinline' if 'subjectinline' mode is not possible but every
   395                 rdef = rschema.role_rdef(eschema, tschema, role)
   416           possible object cardinalities are 1 or ?
   396                 if rdef.may_have_permission(action, req):
   417         * None if neither 'subjectinline' and 'objectinline'
   397                     return True
   418         """
   398         else:
   419         assert not self.final
   399             for rdef in self.rdefs.itervalues():
   420         return self.inlined and 'subjectinline' or None
   400                 if rdef.may_have_permission(action, req):
   421 
   401                     return True
   422     def check_perm(self, session, action, *args, **kwargs):
   402         return False
   423         # NB: session may be a server session or a request object check user is
   403 
   424         # in an allowed group, if so that's enough internal sessions should
   404     def has_perm(self, session, action, **kwargs):
   425         # always stop there
   405         """return true if the action is granted globaly or localy"""
   426         if session.user.matching_groups(self.get_groups(action)):
   406         if 'fromeid' in kwargs:
   427             return
   407             subjtype = session.describe(kwargs['fromeid'])
   428         # else if there is some rql expressions, check them
   408         else:
   429         if any(rqlexpr.check(session, *args, **kwargs)
   409             subjtype = None
   430                for rqlexpr in self.get_rqlexprs(action)):
   410         if 'toeid' in kwargs:
   431             return
   411             objtype = session.describe(kwargs['toeid'])
   432         raise Unauthorized(action, str(self))
   412         else:
   433 
   413             objtype = Nono
   434     def rql_expression(self, expression, mainvars=None, eid=None):
   414         if objtype and subjtype:
   435         """rql expression factory"""
   415             return self.rdef(subjtype, objtype).has_perm(session, action, **kwargs)
   436         if self.final:
   416         elif subjtype:
   437             return ERQLExpression(expression, mainvars, eid)
   417             for tschema in rschema.targets(subjtype, 'subject'):
   438         return RRQLExpression(expression, mainvars, eid)
   418                 rdef = rschema.rdef(subjtype, tschema)
       
   419                 if not rdef.has_perm(action, req, **kwargs):
       
   420                     return False
       
   421         elif objtype:
       
   422             for tschema in rschema.targets(objtype, 'object'):
       
   423                 rdef = rschema.rdef(tschema, objtype)
       
   424                 if not rdef.has_perm(action, req, **kwargs):
       
   425                     return False
       
   426         else:
       
   427             for rdef in self.rdefs.itervalues():
       
   428                 if not rdef.has_perm(action, req, **kwargs):
       
   429                     return False
       
   430 
       
   431     @deprecated('use .rdef(subjtype, objtype).role_cardinality(role)')
       
   432     def cardinality(self, subjtype, objtype, target):
       
   433         return self.rdef(subjtype, objtype).role_cardinality(target)
   439 
   434 
   440 
   435 
   441 class CubicWebSchema(Schema):
   436 class CubicWebSchema(Schema):
   442     """set of entities and relations schema defining the possible data sets
   437     """set of entities and relations schema defining the possible data sets
   443     used in an application
   438     used in an application
   458         self._eid_index = {}
   453         self._eid_index = {}
   459         super(CubicWebSchema, self).__init__(*args, **kwargs)
   454         super(CubicWebSchema, self).__init__(*args, **kwargs)
   460         ybo.register_base_types(self)
   455         ybo.register_base_types(self)
   461         rschema = self.add_relation_type(ybo.RelationType('eid'))
   456         rschema = self.add_relation_type(ybo.RelationType('eid'))
   462         rschema.final = True
   457         rschema.final = True
   463         rschema.set_default_groups()
       
   464         rschema = self.add_relation_type(ybo.RelationType('has_text'))
   458         rschema = self.add_relation_type(ybo.RelationType('has_text'))
   465         rschema.final = True
   459         rschema.final = True
   466         rschema.set_default_groups()
       
   467         rschema = self.add_relation_type(ybo.RelationType('identity'))
   460         rschema = self.add_relation_type(ybo.RelationType('identity'))
   468         rschema.final = False
   461         rschema.final = False
   469         rschema.set_default_groups()
       
   470 
   462 
   471     def add_entity_type(self, edef):
   463     def add_entity_type(self, edef):
   472         edef.name = edef.name.encode()
   464         edef.name = edef.name.encode()
   473         edef.name = bw_normalize_etype(edef.name)
   465         edef.name = bw_normalize_etype(edef.name)
   474         assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name)
   466         assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name)
   506         :param: the newly created or just completed relation schema
   498         :param: the newly created or just completed relation schema
   507         """
   499         """
   508         rdef.name = rdef.name.lower()
   500         rdef.name = rdef.name.lower()
   509         rdef.subject = bw_normalize_etype(rdef.subject)
   501         rdef.subject = bw_normalize_etype(rdef.subject)
   510         rdef.object = bw_normalize_etype(rdef.object)
   502         rdef.object = bw_normalize_etype(rdef.object)
   511         if super(CubicWebSchema, self).add_relation_def(rdef):
   503         rdefs = super(CubicWebSchema, self).add_relation_def(rdef)
       
   504         if rdefs:
   512             try:
   505             try:
   513                 self._eid_index[rdef.eid] = (self.eschema(rdef.subject),
   506                 self._eid_index[rdef.eid] = rdefs
   514                                              self.rschema(rdef.name),
       
   515                                              self.eschema(rdef.object))
       
   516             except AttributeError:
   507             except AttributeError:
   517                 pass # not a serialized schema
   508                 pass # not a serialized schema
   518 
   509 
   519     def del_relation_type(self, rtype):
   510     def del_relation_type(self, rtype):
   520         rschema = self.rschema(rtype)
   511         rschema = self.rschema(rtype)
   521         self._eid_index.pop(rschema.eid, None)
   512         self._eid_index.pop(rschema.eid, None)
   522         super(CubicWebSchema, self).del_relation_type(rtype)
   513         super(CubicWebSchema, self).del_relation_type(rtype)
   523 
   514 
   524     def del_relation_def(self, subjtype, rtype, objtype):
   515     def del_relation_def(self, subjtype, rtype, objtype):
   525         for k, v in self._eid_index.items():
   516         for k, v in self._eid_index.items():
   526             if v == (subjtype, rtype, objtype):
   517             if not isinstance(v, RelationDefinitionSchema):
       
   518                 continue
       
   519             if v.subject == subjtype and v.rtype == rtype and v.object == objtype:
   527                 del self._eid_index[k]
   520                 del self._eid_index[k]
   528                 break
   521                 break
   529         super(CubicWebSchema, self).del_relation_def(subjtype, rtype, objtype)
   522         super(CubicWebSchema, self).del_relation_def(subjtype, rtype, objtype)
   530 
   523 
   531     def del_entity_type(self, etype):
   524     def del_entity_type(self, etype):
   651 
   644 
   652     def __str__(self):
   645     def __str__(self):
   653         return self.full_rql
   646         return self.full_rql
   654     def __repr__(self):
   647     def __repr__(self):
   655         return '%s(%s)' % (self.__class__.__name__, self.full_rql)
   648         return '%s(%s)' % (self.__class__.__name__, self.full_rql)
       
   649 
       
   650     def __cmp__(self, other):
       
   651         if hasattr(other, 'expression'):
       
   652             return cmp(other.expression, self.expression)
       
   653         return False
   656 
   654 
   657     def __deepcopy__(self, memo):
   655     def __deepcopy__(self, memo):
   658         return self.__class__(self.expression, self.mainvars)
   656         return self.__class__(self.expression, self.mainvars)
   659     def __getstate__(self):
   657     def __getstate__(self):
   660         return (self.expression, self.mainvars)
   658         return (self.expression, self.mainvars)
   753             get_eschema = session.vreg.schema.eschema
   751             get_eschema = session.vreg.schema.eschema
   754             try:
   752             try:
   755                 for eaction, var, col in has_perm_defs:
   753                 for eaction, var, col in has_perm_defs:
   756                     for i in xrange(len(rset)):
   754                     for i in xrange(len(rset)):
   757                         eschema = get_eschema(rset.description[i][col])
   755                         eschema = get_eschema(rset.description[i][col])
   758                         eschema.check_perm(session, eaction, rset[i][col])
   756                         eschema.check_perm(session, eaction, eid=rset[i][col])
   759                 if self.eid is not None:
   757                 if self.eid is not None:
   760                     session.local_perm_cache[key] = True
   758                     session.local_perm_cache[key] = True
   761                 return True
   759                 return True
   762             except Unauthorized:
   760             except Unauthorized:
   763                 pass
   761                 pass