server/querier.py
changeset 5423 e15abfdcce38
parent 5177 395e1ff018ae
parent 5421 8167de96c523
child 5426 0d4853a6e5ee
equal deleted inserted replaced
5412:27249e3fee3d 5423:e15abfdcce38
       
     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
   317         the empty tuple key.
   346         the empty tuple key.
   318 
   347 
   319         note: rqlst should not have been simplified at this point
   348         note: rqlst should not have been simplified at this point
   320         """
   349         """
   321         session = self.session
   350         session = self.session
   322         user = session.user
       
   323         schema = self.schema
       
   324         msgs = []
   351         msgs = []
   325         neweids = session.transaction_data.get('neweids', ())
   352         neweids = session.transaction_data.get('neweids', ())
   326         varkwargs = {}
   353         varkwargs = {}
   327         if not session.transaction_data.get('security-rqlst-cache'):
   354         if not session.transaction_data.get('security-rqlst-cache'):
   328             for var in rqlst.defined_vars.itervalues():
   355             for var in rqlst.defined_vars.itervalues():
   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