[wip] Introduce a security_ctx in rqlrewrite

This patch is only a proof-of-concept which demonstrate how the rqlrewriter could make use of variables from security context.

Related to #4919855

authorChristophe de Vienne <christophe@unlish.com>
changesetdf655cf48679
branchdefault
phasedraft
hiddenyes
parent revision#a0a11be5a9cb [schema] set permissions that do not allow edition on computed relation. Closes #4903918
child revision#dd582ac2a037 [wip] Add access to the security_context from subsitute variables, #32e0fef22a18 [wip] Add access to the security_context from subsitute variables
files modified by this revision
rqlrewrite.py
test/unittest_rqlrewrite.py
# HG changeset patch
# User Christophe de Vienne <christophe@unlish.com>
# Date 1423174237 -3600
# Thu Feb 05 23:10:37 2015 +0100
# Node ID df655cf48679c0863b660908b72467c2bde084cf
# Parent a0a11be5a9cb982e2d95eceb33ee043c5cc336ee
[wip] Introduce a security_ctx in rqlrewrite

This patch is only a proof-of-concept which demonstrate how the rqlrewriter
could make use of variables from security context.

Related to #4919855

diff --git a/rqlrewrite.py b/rqlrewrite.py
@@ -223,16 +223,17 @@
1      inserted under an EXISTS node.
2 
3      This class *isn't thread safe*.
4      """
5 
6 -    def __init__(self, session):
7 +    def __init__(self, session, security_ctx=None):
8          self.session = session
9          vreg = session.vreg
10          self.schema = vreg.schema
11          self.annotate = vreg.rqlhelper.annotate
12          self._compute_solutions = vreg.solutions
13 +        self.security_ctx = security_ctx
14 
15      def compute_solutions(self):
16          self.annotate(self.select)
17          try:
18              self._compute_solutions(self.session, self.select, self.kwargs)
@@ -761,10 +762,20 @@
19                  stmt.add_constant_restriction(
20                      stmt.get_variable(self.u_varname),
21                      'eid', unicode(argname), 'Substitute')
22                  self.kwargs[argname] = self.session.user.eid
23              return self.u_varname
24 +        if vname.startswith('CTX_'):
25 +            # handle final entities
26 +            value = self.security_ctx.get(vname[4:].lower())
27 +            if isinstance(value, basestring):
28 +                c_type = 'String'
29 +            # XXX handle other types, maybe add the possibility to force
30 +            # in the ctx.
31 +            # XXX It may be easier/better to return argument
32 +            return n.Constant(value, c_type)
33 +            # handle non-final entities
34          key = (self.current_expr, self.varmap, vname)
35          try:
36              return self.rewritten[key]
37          except KeyError:
38              self.rewritten[key] = newvname = stmt.allocate_varname()
diff --git a/test/unittest_rqlrewrite.py b/test/unittest_rqlrewrite.py
@@ -46,11 +46,11 @@
39  def eid_func_map(eid):
40      return {1: 'CWUser',
41              2: 'Card',
42              3: 'Affaire'}[eid]
43 
44 -def _prepare_rewriter(rewriter_cls, kwargs):
45 +def _prepare_rewriter(rewriter_cls, kwargs, security_ctx=None):
46      class FakeVReg:
47          schema = schema
48          @staticmethod
49          def solutions(sqlcursor, rqlst, kwargs):
50              rqlhelper.compute_solutions(rqlst, {'eid': eid_func_map}, kwargs=kwargs)
@@ -59,14 +59,15 @@
51              def annotate(rqlst):
52                  rqlhelper.annotate(rqlst)
53              @staticmethod
54              def simplify(mainrqlst, needcopy=False):
55                  rqlhelper.simplify(rqlst, needcopy)
56 -    return rewriter_cls(mock_object(vreg=FakeVReg, user=(mock_object(eid=1))))
57 +    return rewriter_cls(
58 +        mock_object(vreg=FakeVReg, user=(mock_object(eid=1))), security_ctx)
59 
60 -def rewrite(rqlst, snippets_map, kwargs, existingvars=None):
61 -    rewriter = _prepare_rewriter(rqlrewrite.RQLRewriter, kwargs)
62 +def rewrite(rqlst, snippets_map, kwargs, existingvars=None, security_ctx=None):
63 +    rewriter = _prepare_rewriter(rqlrewrite.RQLRewriter, kwargs, security_ctx)
64      snippets = []
65      for v, exprs in sorted(snippets_map.items()):
66          rqlexprs = [isinstance(snippet, basestring)
67                      and mock_object(snippet_rqlst=parse('Any X WHERE '+snippet).children[0],
68                                      expression='Any X WHERE '+snippet)
@@ -113,10 +114,20 @@
69          self.assertEqual(rqlst.as_string(),
70                           u"Any C WHERE C is Card, B eid %(D)s, "
71                           "EXISTS(C in_state A, B in_group E, F require_state A, "
72                           "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission)")
73 
74 +    def test_security_ctx_var(self):
75 +        constraint = ('X owned_by USER, USER in_group G, G name CTX_MAINGROUP')
76 +        rqlst = parse('Card C')
77 +        rewrite(rqlst, {('C', 'X'): (constraint,)},
78 +                {}, security_ctx={'maingroup': 'agroupname'})
79 +        self.assertEqual(rqlst.as_string(),
80 +                         u"Any C WHERE C is Card, "
81 +                         "EXISTS(C owned_by A, A in_group B, "
82 +                         "B name 'agroupname', A is CWUser, B is CWGroup)")
83 +
84      def test_multiple_var(self):
85          card_constraint = ('X in_state S, U in_group G, P require_state S,'
86                             'P name "read", P require_group G')
87          affaire_constraints = ('X ref LIKE "PUBLIC%"', 'U in_group G, G name "public"')
88          kwargs = {'u':2}