def marshal_write_response(document, resource): """ Limit response document to minimize bandwidth when client supports it. :param document: the response document. :param resource: the resource being consumed by the request. .. versionchanged: 0.5 Avoid exposing 'auth_field' if it is not intended to be public. .. versionadded:: 0.4 """ resource_def = app.config['DOMAIN'][resource] if app.config['BANDWIDTH_SAVER'] is True: # only return the automatic fields and special extra fields fields = auto_fields(resource) + resource_def['extra_response_fields'] document = dict((k, v) for (k, v) in document.items() if k in fields) else: # avoid exposing the auth_field if it is not included in the # resource schema. auth_field = resource_def.get('auth_field') if auth_field and auth_field not in resource_def['schema']: try: del(document[auth_field]) except: # 'auth_field' value has not been set by the auth class. pass return document
def marshal_write_response(document, resource): """ Limit response document to minimize bandwidth when client supports it. :param document: the response document. :param resource: the resource being consumed by the request. .. versionchanged: 0.5 Avoid exposing 'auth_field' if it is not intended to be public. .. versionadded:: 0.4 """ resource_def = app.config['DOMAIN'][resource] if app.config['BANDWIDTH_SAVER'] is True: # only return the automatic fields and special extra fields fields = auto_fields(resource) + resource_def['extra_response_fields'] document = dict((k, v) for (k, v) in document.items() if k in fields) else: # avoid exposing the auth_field if it is not included in the # resource schema. auth_field = resource_def.get('auth_field') if auth_field and auth_field not in resource_def['schema']: try: del (document[auth_field]) except: # 'auth_field' value has not been set by the auth class. pass return document
def insert(self, resource, doc_or_docs): #if isinstance(doc_or_docs, list): TODO # if upsert.. ids = [] for doc in doc_or_docs: keys = self.repo[resource].keys() id = 1 if len(keys) == 0 else int(max(keys))+1 for f in auto_fields(resource): if f in doc: del doc[f] doc[config.ID_FIELD] = id self.repo[resource][id] = doc ids.append(id) self._write() return ids
def marshal_write_response(document, resource): """ Limit response document to minimize bandwidth when client supports it. :param document: the response document. :param resource: the resource being consumed by the request. .. versionadded:: 0.4 """ if app.config['BANDWIDTH_SAVER'] is True: # only return the automatic fields and special extra fields fields = auto_fields(resource) + \ app.config['DOMAIN'][resource]['extra_response_fields'] document = dict((k, v) for (k, v) in document.items() if k in fields) return document
def _datasource_ex(self, resource, query=None, client_projection=None, client_sort=None, check_auth_value=True, force_auth_field_projection=False): """ Returns both db collection and exact query (base filter included) to which an API resource refers to. .. versionchanged:: 0.5.2 Make User Restricted Resource Access work with HMAC Auth too. .. versionchanged:: 0.5 Let client projection work when 'allow_unknown' is active (#497). .. versionchanged:: 0.4 Always return required/auto fields (issue 282.) .. versionchanged:: 0.3 Field exclusion support in client projections. Honor auth_field even when client query is missing. Only inject auth_field in queries when we are not creating new documents. 'auth_field' and 'request_auth_value' fetching is now delegated to auth.auth_field_and value(). .. versionchanged:: 0.2 Difference between resource and item endpoints is now determined by the presence of a '|' in request.endpoint. Support for 'default_sort'. .. versionchanged:: 0.1.1 auth.request_auth_value is now used to store the auth_field value. .. versionchanged:: 0.1.0 Calls `combine_queries` to merge query and filter_ Updated logic performing `auth_field` check .. versionchanged:: 0.0.9 Storing self.app.auth.userid in auth_field when 'user-restricted resource access' is enabled. Support for Python 3.3. .. versionchanged:: 0.0.6 'auth_username_field' is injected even in empty queries. Projection queries ('?projection={"name": 1}') .. versionchanged:: 0.0.5 Support for 'user-restricted resource access'. .. versionadded:: 0.0.4 """ datasource, filter_, projection_, sort_ = self.datasource(resource) if client_sort: sort = client_sort else: # default sort is activated only if 'sorting' is enabled for the # resource. # TODO Consider raising a validation error on startup instead? sort = sort_ if sort_ and config.DOMAIN[resource]['sorting'] else \ None if filter_: if query: # Can't just dump one set of query operators into another # e.g. if the dataset contains a custom datasource pattern # 'filter': {'username': {'$exists': True}} # and we try to filter on the field `username`, # which is correct? # Solution: call the db driver `combine_queries` operation # which will apply db-specific syntax to produce the # intersection of the two queries query = self.combine_queries(query, filter_) else: query = filter_ fields = projection_ if client_projection: if projection_: # only allow fields which are included with the standard # projection for the resource (avoid sniffing of private # fields) keep_fields = auto_fields(resource) if 1 in client_projection.values(): # inclusive projection - all values are 0 unless spec. or # auto fields = dict([(field, field in keep_fields) for field in fields.keys()]) for field, value in client_projection.items(): field_base = field.split('.')[0] if field_base not in keep_fields and field_base in fields: fields[field] = value else: # there's no standard projection so we assume we are in a # allow_unknown = True fields = client_projection # always drop exclusion projection, thus avoid mixed projection not # supported by db driver fields = dict([(field, 1) for field, value in fields.items() if value]) # If the current HTTP method is in `public_methods` or # `public_item_methods`, skip the `auth_field` check # Only inject the auth_field in the query when not creating new # documents. if request and request.method != 'POST' and ( check_auth_value or force_auth_field_projection): auth_field, request_auth_value = auth_field_and_value(resource) if auth_field: if request_auth_value and check_auth_value: if query: # If the auth_field *replaces* a field in the query, # and the values are /different/, deny the request # This prevents the auth_field condition from # overwriting the query (issue #77) auth_field_in_query = \ self.app.data.query_contains_field(query, auth_field) if auth_field_in_query and \ self.app.data.get_value_from_query( query, auth_field) != request_auth_value: desc = 'Incompatible User-Restricted Resource ' \ 'request.' abort(401, description=desc) else: query = self.app.data.combine_queries( query, {auth_field: request_auth_value}) else: query = {auth_field: request_auth_value} if force_auth_field_projection: fields[auth_field] = 1 return datasource, query, fields, sort
def _datasource_ex(self, resource, query=None, client_projection=None, client_sort=None): """ Returns both db collection and exact query (base filter included) to which an API resource refers to. .. versionchanged:: 0.4 Always return required/auto fields (issue 282.) .. versionchanged:: 0.3 Field exclusion support in client projections. Honor auth_field even when client query is missing. Only inject auth_field in queries when we are not creating new documents. 'auth_field' and 'request_auth_value' fetching is now delegated to auth.auth_field_and value(). .. versionchanged:: 0.2 Difference between resource and item endpoints is now determined by the presence of a '|' in request.endpoint. Support for 'default_sort'. .. versionchanged:: 0.1.1 auth.request_auth_value is now used to store the auth_field value. .. versionchanged:: 0.1.0 Calls `combine_queries` to merge query and filter_ Updated logic performing `auth_field` check .. versionchanged:: 0.0.9 Storing self.app.auth.userid in auth_field when 'user-restricted resource access' is enabled. Support for Python 3.3. .. versionchanged:: 0.0.6 'auth_username_field' is injected even in empty queries. Projection queries ('?projection={"name": 1}') .. versionchanged:: 0.0.5 Support for 'user-restricted resource access'. .. versionadded:: 0.0.4 """ datasource, filter_, projection_, sort_ = self._datasource(resource) if client_sort: sort = client_sort else: # default sort is activated only if 'sorting' is enabled for the # resource. # TODO Consider raising a validation error on startup instead? sort = sort_ if sort_ and config.DOMAIN[resource]['sorting'] else \ None if filter_: if query: # Can't just dump one set of query operators into another # e.g. if the dataset contains a custom datasource pattern # 'filter': {'username': {'$exists': True}} # and we try to filter on the field `username`, # which is correct? # Solution: call the db driver `combine_queries` operation # which will apply db-specific syntax to produce the # intersection of the two queries query = self.combine_queries(query, filter_) else: query = filter_ fields = projection_ if client_projection: # only allow fields which are included with the standard projection # for the resource (avoid sniffing of private fields) keep_fields = auto_fields(resource) if 0 not in client_projection.values(): # inclusive projection - all values are 0 unless spec. or auto fields = dict([(field, field in keep_fields) for field in fields.keys()]) for field, value in client_projection.items(): field_base = field.split('.')[0] if field_base not in keep_fields and field_base in fields: fields[field] = value fields = dict([(field, 1) for field, value in fields.items() if value]) # If the current HTTP method is in `public_methods` or # `public_item_methods`, skip the `auth_field` check # Only inject the auth_field in the query when not creating new # documents. if request and request.method not in ('POST', 'PUT'): auth_field, request_auth_value = auth_field_and_value(resource) if auth_field and request.authorization and request_auth_value: if query: # If the auth_field *replaces* a field in the query, # and the values are /different/, deny the request # This prevents the auth_field condition from # overwriting the query (issue #77) auth_field_in_query = \ self.app.data.query_contains_field(query, auth_field) if auth_field_in_query and \ self.app.data.get_value_from_query( query, auth_field) != request_auth_value: abort(401, description=debug_error_message( 'Incompatible User-Restricted Resource request. ' 'Request was for "%s"="%s" but `auth_field` ' 'requires "%s"="%s".' % ( auth_field, self.app.data.get_value_from_query( query, auth_field), auth_field, request_auth_value) )) else: query = self.app.data.combine_queries( query, {auth_field: request_auth_value} ) else: query = {auth_field: request_auth_value} return datasource, query, fields, sort
def _find(self, resource, req, **lookup): sort = [] spec = {} model = self._get_model_cls(resource) if req: if req.where: # could map mongo-style and_, or_ to peewee ops for eve/sqla compatibility try: spec = json.loads(req.where) except ValueError as exc: self.app.logger.exception(exc) abort(400, description='Unable to parse `where` clause') if config.VALIDATE_FILTERS: bad_filter = validate_filters(spec, resource) if bad_filter: abort(400, bad_filter) if config.DOMAIN[resource]['soft_delete'] and not req.show_deleted: # Soft delete filtering applied after validate_filters call as # querying against the DELETED field must always be allowed when # soft_delete is enabled #spec[config.DELETED+'__ne'] = True if not self.query_contains_field(spec, config.DELETED): spec = self.combine_queries(spec, {config.DELETED+'__ne': True}) if req.sort: for sort_arg in [s.strip() for s in req.sort.split(",")]: sn = sort_arg[1:] if sort_arg[0] == "-" else sort_arg try: if sort_arg[0] == "-": sort.append(getattr(model, sn).desc()) else: sort.append(getattr(model, sn)) except AttributeError: abort(400, description='Unknown field name: %s' % sn) if 'lookup' in lookup and lookup['lookup']: spec = self.combine_queries( spec, lookup['lookup']) spec = lookup['lookup'] client_projection = self._client_projection(req) datasource, spec, projection, sort = self._datasource_ex( resource, spec, client_projection, sort) # TODO? http://eve-sqlalchemy.readthedocs.org/en/latest/tutorial.html#embedded-resources if len(projection): fields = [getattr(model, config.ID_FIELD)] exclude_only = all(not v for v in projection.values()) include_only = all(projection.values()) keep_fields = auto_fields(resource) for f in self.models[resource]._meta.fields.keys(): if f == config.ID_FIELD: continue check_list = [include_only and f in projection, exclude_only and f not in projection, f in projection and projection[f]] # if not an auto_field and not projected out if f not in keep_fields and not any(check_list): continue fields.append(getattr(model, f)) op = model.select(*fields) else: op = model.select() op = self._parse_where(op, spec) if sort: def fix_sort(sort_arg): # default sort takes [('fname',1)] if not isinstance(sort_arg, tuple): return sort_arg sn,asc = sort_arg sortf = getattr(model, sn) if not asc: sortf = sortf.desc() return sortf sort = map(fix_sort, sort) op = op.order_by(*sort) return op
def _datasource_ex(self, resource, query=None, client_projection=None, client_sort=None): """ Returns both db collection and exact query (base filter included) to which an API resource refers to. .. versionchanged:: 0.4 Always return required/auto fields (issue 282.) .. versionchanged:: 0.3 Field exclusion support in client projections. Honor auth_field even when client query is missing. Only inject auth_field in queries when we are not creating new documents. 'auth_field' and 'request_auth_value' fetching is now delegated to auth.auth_field_and value(). .. versionchanged:: 0.2 Difference between resource and item endpoints is now determined by the presence of a '|' in request.endpoint. Support for 'default_sort'. .. versionchanged:: 0.1.1 auth.request_auth_value is now used to store the auth_field value. .. versionchanged:: 0.1.0 Calls `combine_queries` to merge query and filter_ Updated logic performing `auth_field` check .. versionchanged:: 0.0.9 Storing self.app.auth.userid in auth_field when 'user-restricted resource access' is enabled. Support for Python 3.3. .. versionchanged:: 0.0.6 'auth_username_field' is injected even in empty queries. Projection queries ('?projection={"name": 1}') .. versionchanged:: 0.0.5 Support for 'user-restricted resource access'. .. versionadded:: 0.0.4 """ datasource, filter_, projection_, sort_ = self._datasource(resource) if client_sort: sort = client_sort else: # default sort is activated only if 'sorting' is enabled for the # resource. # TODO Consider raising a validation error on startup instead? sort = sort_ if sort_ and config.DOMAIN[resource]['sorting'] else \ None if filter_: if query: # Can't just dump one set of query operators into another # e.g. if the dataset contains a custom datasource pattern # 'filter': {'username': {'$exists': True}} # and we try to filter on the field `username`, # which is correct? # Solution: call the db driver `combine_queries` operation # which will apply db-specific syntax to produce the # intersection of the two queries query = self.combine_queries(query, filter_) else: query = filter_ fields = projection_ keep_fields = auto_fields(resource) if client_projection: # only allow fields which are included with the standard projection # for the resource (avoid sniffing of private fields) if 0 in client_projection.values(): # exclusive projection - all values are 1 unless specified for field, value in client_projection.items(): if value == 0 and value not in keep_fields and \ field in fields: del fields[field] else: # inclusive projection - all values are 0 unless spec. or auto for field in list(fields.keys()): if field not in client_projection and \ field not in keep_fields: del fields[field] # If the current HTTP method is in `public_methods` or # `public_item_methods`, skip the `auth_field` check # Only inject the auth_field in the query when not creating new # documents. if request and request.method not in ('POST', 'PUT'): auth_field, request_auth_value = auth_field_and_value(resource) if auth_field and request.authorization and request_auth_value: if query: # If the auth_field *replaces* a field in the query, # and the values are /different/, deny the request # This prevents the auth_field condition from # overwriting the query (issue #77) auth_field_in_query = \ self.app.data.query_contains_field(query, auth_field) if auth_field_in_query and \ self.app.data.get_value_from_query( query, auth_field) != request_auth_value: abort(401, description=debug_error_message( 'Incompatible User-Restricted Resource request. ' 'Request was for "%s"="%s" but `auth_field` ' 'requires "%s"="%s".' % ( auth_field, self.app.data.get_value_from_query( query, auth_field), auth_field, request_auth_value) )) else: query = self.app.data.combine_queries( query, {auth_field: request_auth_value} ) else: query = {auth_field: request_auth_value} return datasource, query, fields, sort