|
1 # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
3 # |
|
4 # This file is part of CubicWeb. |
|
5 # |
|
6 # CubicWeb is free software: you can redistribute it and/or modify it under the |
|
7 # terms of the GNU Lesser General Public License as published by the Free |
|
8 # Software Foundation, either version 2.1 of the License, or (at your option) |
|
9 # any later version. |
|
10 # |
|
11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT |
|
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
|
14 # details. |
|
15 # |
|
16 # You should have received a copy of the GNU Lesser General Public License along |
|
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
1 """Helper classes to execute RQL queries on a set of sources, performing |
18 """Helper classes to execute RQL queries on a set of sources, performing |
2 security checking and data aggregation. |
19 security checking and data aggregation. |
3 |
20 |
4 :organization: Logilab |
|
5 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
|
6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
7 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
|
8 """ |
21 """ |
9 from __future__ import with_statement |
22 from __future__ import with_statement |
10 |
23 |
11 __docformat__ = "restructuredtext en" |
24 __docformat__ = "restructuredtext en" |
12 |
25 |
46 """check that Password entities are not selected""" |
59 """check that Password entities are not selected""" |
47 for solution in rqlst.solutions: |
60 for solution in rqlst.solutions: |
48 if 'Password' in solution.itervalues(): |
61 if 'Password' in solution.itervalues(): |
49 raise Unauthorized('Password selection is not allowed') |
62 raise Unauthorized('Password selection is not allowed') |
50 |
63 |
51 def check_read_access(schema, user, rqlst, solution): |
64 def term_etype(session, term, solution, args): |
|
65 """return the entity type for the given term (a VariableRef or a Constant |
|
66 node) |
|
67 """ |
|
68 try: |
|
69 return solution[term.name] |
|
70 except AttributeError: |
|
71 return session.describe(term.eval(args))[0] |
|
72 |
|
73 def check_read_access(session, rqlst, solution, args): |
52 """check that the given user has credentials to access data read the |
74 """check that the given user has credentials to access data read the |
53 query |
75 query |
54 |
76 |
55 return a dict defining necessary local checks (due to use of rql expression |
77 return a dict defining necessary local checks (due to use of rql expression |
56 in the schema), keys are variable names and values associated rql expression |
78 in the schema), keys are variable names and values associated rql expression |
57 for the associated variable with the given solution |
79 for the associated variable with the given solution |
58 """ |
80 """ |
|
81 # use `term_etype` since we've to deal with rewritten constants here, |
|
82 # when used as an external source by another repository. |
|
83 # XXX what about local read security w/ those rewritten constants... |
|
84 schema = session.repo.schema |
59 if rqlst.where is not None: |
85 if rqlst.where is not None: |
60 for rel in rqlst.where.iget_nodes(Relation): |
86 for rel in rqlst.where.iget_nodes(Relation): |
61 # XXX has_text may have specific perm ? |
87 # XXX has_text may have specific perm ? |
62 if rel.r_type in READ_ONLY_RTYPES: |
88 if rel.r_type in READ_ONLY_RTYPES: |
63 continue |
89 continue |
64 rschema = schema.rschema(rel.r_type) |
90 rschema = schema.rschema(rel.r_type) |
65 if rschema.final: |
91 if rschema.final: |
66 eschema = schema.eschema(solution[rel.children[0].name]) |
92 eschema = schema.eschema(term_etype(session, rel.children[0], |
|
93 solution, args)) |
67 rdef = eschema.rdef(rschema) |
94 rdef = eschema.rdef(rschema) |
68 else: |
95 else: |
69 rdef = rschema.rdef(solution[rel.children[0].name], |
96 rdef = rschema.rdef(term_etype(session, rel.children[0], |
70 solution[rel.children[1].children[0].name]) |
97 solution, args), |
71 if not user.matching_groups(rdef.get_groups('read')): |
98 term_etype(session, rel.children[1].children[0], |
|
99 solution, args)) |
|
100 if not session.user.matching_groups(rdef.get_groups('read')): |
72 # XXX rqlexpr not allowed |
101 # XXX rqlexpr not allowed |
73 raise Unauthorized('read', rel.r_type) |
102 raise Unauthorized('read', rel.r_type) |
74 localchecks = {} |
103 localchecks = {} |
75 # iterate on defined_vars and not on solutions to ignore column aliases |
104 # iterate on defined_vars and not on solutions to ignore column aliases |
76 for varname in rqlst.defined_vars: |
105 for varname in rqlst.defined_vars: |
77 eschema = schema.eschema(solution[varname]) |
106 eschema = schema.eschema(solution[varname]) |
78 if eschema.final: |
107 if eschema.final: |
79 continue |
108 continue |
80 if not user.matching_groups(eschema.get_groups('read')): |
109 if not session.user.matching_groups(eschema.get_groups('read')): |
81 erqlexprs = eschema.get_rqlexprs('read') |
110 erqlexprs = eschema.get_rqlexprs('read') |
82 if not erqlexprs: |
111 if not erqlexprs: |
83 ex = Unauthorized('read', solution[varname]) |
112 ex = Unauthorized('read', solution[varname]) |
84 ex.var = varname |
113 ex.var = varname |
85 raise ex |
114 raise ex |
333 localchecks = {} |
360 localchecks = {} |
334 restricted_vars = set() |
361 restricted_vars = set() |
335 newsolutions = [] |
362 newsolutions = [] |
336 for solution in rqlst.solutions: |
363 for solution in rqlst.solutions: |
337 try: |
364 try: |
338 localcheck = check_read_access(schema, user, rqlst, solution) |
365 localcheck = check_read_access(session, rqlst, solution, self.args) |
339 except Unauthorized, ex: |
366 except Unauthorized, ex: |
340 msg = 'remove %s from solutions since %s has no %s access to %s' |
367 msg = 'remove %s from solutions since %s has no %s access to %s' |
341 msg %= (solution, user.login, ex.args[0], ex.args[1]) |
368 msg %= (solution, session.user.login, ex.args[0], ex.args[1]) |
342 msgs.append(msg) |
369 msgs.append(msg) |
343 LOGGER.info(msg) |
370 LOGGER.info(msg) |
344 else: |
371 else: |
345 newsolutions.append(solution) |
372 newsolutions.append(solution) |
346 # try to benefit of rqlexpr.check cache for entities which |
373 # try to benefit of rqlexpr.check cache for entities which |