13 :organization: Logilab |
13 :organization: Logilab |
14 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
14 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
15 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
15 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
16 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
16 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
17 """ |
17 """ |
|
18 from __future__ import with_statement |
|
19 |
18 __docformat__ = "restructuredtext en" |
20 __docformat__ = "restructuredtext en" |
19 |
21 |
20 import sys |
22 import sys |
21 import Queue |
23 import Queue |
22 from os.path import join |
24 from os.path import join |
34 ETypeNotSupportedBySources, MultiSourcesError, |
36 ETypeNotSupportedBySources, MultiSourcesError, |
35 BadConnectionId, Unauthorized, ValidationError, |
37 BadConnectionId, Unauthorized, ValidationError, |
36 typed_eid) |
38 typed_eid) |
37 from cubicweb import cwvreg, schema, server |
39 from cubicweb import cwvreg, schema, server |
38 from cubicweb.server import utils, hook, pool, querier, sources |
40 from cubicweb.server import utils, hook, pool, querier, sources |
39 from cubicweb.server.session import Session, InternalSession |
41 from cubicweb.server.session import Session, InternalSession, security_enabled |
40 |
42 |
41 |
43 |
42 class CleanupEidTypeCacheOp(hook.SingleLastOperation): |
44 class CleanupEidTypeCacheOp(hook.SingleLastOperation): |
43 """on rollback of a insert query or commit of delete query, we have to |
45 """on rollback of a insert query or commit of delete query, we have to |
44 clear repository's cache from no more valid entries |
46 clear repository's cache from no more valid entries |
78 an entity without a relation for some time |
80 an entity without a relation for some time |
79 |
81 |
80 this kind of behaviour has to be done in the repository so we don't have |
82 this kind of behaviour has to be done in the repository so we don't have |
81 hooks order hazardness |
83 hooks order hazardness |
82 """ |
84 """ |
83 # XXX now that rql in migraction default to unsafe_execute we don't want to |
85 # skip that for internal session or if integrity explicitly disabled |
84 # skip that for super session (though we can still skip it for internal |
86 # |
85 # sessions). Also we should imo rely on the orm to first fetch existing |
87 # XXX we should imo rely on the orm to first fetch existing entity if any |
86 # entity if any then delete it. |
88 # then delete it. |
87 if session.is_internal_session \ |
89 if session.is_internal_session \ |
88 or not session.is_hooks_category_activated('integrity'): |
90 or not session.is_hooks_category_activated('integrity'): |
89 return |
91 return |
90 card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality') |
92 card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality') |
91 # one may be tented to check for neweids but this may cause more than one |
93 # one may be tented to check for neweids but this may cause more than one |
98 # XXX we don't want read permissions to be applied but we want delete |
100 # XXX we don't want read permissions to be applied but we want delete |
99 # permission to be checked |
101 # permission to be checked |
100 rschema = session.repo.schema.rschema(rtype) |
102 rschema = session.repo.schema.rschema(rtype) |
101 if card[0] in '1?': |
103 if card[0] in '1?': |
102 if not rschema.inlined: # inlined relations will be implicitly deleted |
104 if not rschema.inlined: # inlined relations will be implicitly deleted |
103 rset = session.unsafe_execute('Any X,Y WHERE X %s Y, X eid %%(x)s, ' |
105 with security_enabled(session, read=False): |
104 'NOT Y eid %%(y)s' % rtype, |
106 session.execute('DELETE X %s Y WHERE X eid %%(x)s, ' |
105 {'x': eidfrom, 'y': eidto}, 'x') |
107 'NOT Y eid %%(y)s' % rtype, |
106 if rset: |
108 {'x': eidfrom, 'y': eidto}, 'x') |
107 safe_delete_relation(session, rschema, *rset[0]) |
|
108 if card[1] in '1?': |
109 if card[1] in '1?': |
109 rset = session.unsafe_execute('Any X,Y WHERE X %s Y, Y eid %%(y)s, ' |
110 with security_enabled(session, read=False): |
110 'NOT X eid %%(x)s' % rtype, |
111 session.execute('DELETE X %sY WHERE Y eid %%(y)s, ' |
111 {'x': eidfrom, 'y': eidto}, 'y') |
112 'NOT X eid %%(x)s' % rtype, |
112 if rset: |
113 {'x': eidfrom, 'y': eidto}, 'y') |
113 safe_delete_relation(session, rschema, *rset[0]) |
|
114 |
|
115 |
|
116 def safe_delete_relation(session, rschema, subject, object): |
|
117 if not rschema.has_perm(session, 'delete', fromeid=subject, toeid=object): |
|
118 raise Unauthorized() |
|
119 session.repo.glob_delete_relation(session, subject, rschema.type, object) |
|
120 |
114 |
121 |
115 |
122 class Repository(object): |
116 class Repository(object): |
123 """a repository provides access to a set of persistent storages for |
117 """a repository provides access to a set of persistent storages for |
124 entities and relations |
118 entities and relations |
916 its relations |
910 its relations |
917 """ |
911 """ |
918 rql = [] |
912 rql = [] |
919 eschema = self.schema.eschema(etype) |
913 eschema = self.schema.eschema(etype) |
920 pendingrtypes = session.transaction_data.get('pendingrtypes', ()) |
914 pendingrtypes = session.transaction_data.get('pendingrtypes', ()) |
921 for rschema, targetschemas, x in eschema.relation_definitions(): |
915 with security_enabled(session, read=False, write=False): |
922 rtype = rschema.type |
916 for rschema, targetschemas, x in eschema.relation_definitions(): |
923 if rtype in schema.VIRTUAL_RTYPES or rtype in pendingrtypes: |
917 rtype = rschema.type |
924 continue |
918 if rtype in schema.VIRTUAL_RTYPES or rtype in pendingrtypes: |
925 var = '%s%s' % (rtype.upper(), x.upper()) |
919 continue |
926 if x == 'subject': |
920 var = '%s%s' % (rtype.upper(), x.upper()) |
927 # don't skip inlined relation so they are regularly |
921 if x == 'subject': |
928 # deleted and so hooks are correctly called |
922 # don't skip inlined relation so they are regularly |
929 selection = 'X %s %s' % (rtype, var) |
923 # deleted and so hooks are correctly called |
930 else: |
924 selection = 'X %s %s' % (rtype, var) |
931 selection = '%s %s X' % (var, rtype) |
925 else: |
932 rql = 'DELETE %s WHERE X eid %%(x)s' % selection |
926 selection = '%s %s X' % (var, rtype) |
933 # unsafe_execute since we suppose that if user can delete the entity, |
927 rql = 'DELETE %s WHERE X eid %%(x)s' % selection |
934 # he can delete all its relations without security checking |
928 # if user can delete the entity, he can delete all its relations |
935 session.unsafe_execute(rql, {'x': eid}, 'x', build_descr=False) |
929 # without security checking |
|
930 session.execute(rql, {'x': eid}, 'x', build_descr=False) |
936 |
931 |
937 def locate_relation_source(self, session, subject, rtype, object): |
932 def locate_relation_source(self, session, subject, rtype, object): |
938 subjsource = self.source_from_eid(subject, session) |
933 subjsource = self.source_from_eid(subject, session) |
939 objsource = self.source_from_eid(object, session) |
934 objsource = self.source_from_eid(object, session) |
940 if not subjsource is objsource: |
935 if not subjsource is objsource: |