def search_filter_record_permissions(): """Filter list of results by `_access` and `open_access` fields.""" if not has_request_context() or backoffice_permission().allows(g.identity): return Q() # A record is public if `open_access` field True or missing open_access_field_missing = ~Q("exists", field="open_access") is_open_access = open_access_field_missing | Q("term", open_access=True) combined_filter = is_open_access if current_app.config.get("ILS_RECORDS_EXPLICIT_PERMISSIONS_ENABLED"): # if `_access`, check `_access.read` against the user. It takes # precedence over `open_access`. # if not `_access`, check if open access as before. _access_field_exists = Q("exists", field="_access.read") provides = _get_user_provides() user_can_read = _access_field_exists & Q( "terms", **{"_access.read": provides} ) combined_filter = user_can_read | ( ~_access_field_exists & is_open_access ) return Q("bool", filter=[combined_filter])
def post(self, pid, record, **kwargs): """Send a signal to count record view for the record stats.""" factory = RecordPermission(record, "read") if not factory.is_public() and not backoffice_permission().can(): if not current_user.is_authenticated: abort(401) abort(403) data = request.get_json() event_name = data.get("event") if event_name == "record-view": record_viewed.send( current_app._get_current_object(), pid=pid, record=record, ) return self.make_response(pid, record, 202) elif event_name == "file-download": if "key" not in data: abort(406, "File key is required") if "bucket_id" not in record: abort(406, "Record has no bucket") obj = ObjectVersion.get(record["bucket_id"], data["key"]) file_downloaded.send(current_app._get_current_object(), obj=obj, record=record) return self.make_response(pid, record, 202) return StatsError( description="Invalid stats event request: {}".format(event_name))
def _filter_by_current_patron(search, query_string=None): """Filter search results by patron_pid.""" # if the logged in user is not librarian or admin, validate the query if has_request_context() and not backoffice_permission().allows( g.identity): return _filter_by_patron(g.identity.id, search, query_string) return search, query_string
def validate_patron(patron_pid): """Validate patron PID.""" if not backoffice_permission().allows(g.identity): if patron_pid != str(g.identity.id): raise ValidationError( "The authenticated user is not authorized to create or update " "a document request for another patron.")
def loan_permission_filter(): """Filter loans by owner.""" if backoffice_permission().allows(g.identity): return Q() # Filter loans where the user is owner return Q('match', **{'patron_pid': g.identity.id})
def views_permissions_factory(action): """Override ILS views permissions factory.""" if action == "retrieve-patron-loans": return retrieve_patron_loans_permission() elif action == "document-importer": return backoffice_permission() return ils_views_permissions_factory(action)
def circulation_default_extension_max_count(loan): """Return a default extensions max count.""" is_admin_or_librarian = has_request_context() and backoffice_permission( ).allows(g.identity) if is_admin_or_librarian: unlimited = loan.get("extension_count", 0) + 1 return unlimited return 3
def circulation_search_factory(self, search, query_parser=None): """Parse query using elasticsearch DSL query. :param self: REST view. :param search: Elastic search DSL search instance. :returns: Tuple with search instance and URL arguments. """ def _default_parser(qstr=None): """Return default parser that uses the Q() from elasticsearch_dsl.""" if qstr: return Q('query_string', query=qstr) return Q() from invenio_records_rest.facets import default_facets_factory from invenio_records_rest.sorter import default_sorter_factory query_string = request.values.get('q', '') query = _default_parser(qstr=query_string) # if the logged in user in not librarian or admin, validate the query if not backoffice_permission().allows(g.identity): # patron can find only his loans try: if not query_string: # force query to be patron_pid:<logged in user> only_patron_loans = 'patron_pid:{}'.format(g.identity.id) query = _default_parser(qstr=only_patron_loans) else: # check for patron_pid query value match = re.match(r"patron_pid:(?P<pid>\d)", query_string) if match and match.group('pid') != str(g.identity.id): raise UnauthorizedSearch() except UnauthorizedSearch: current_app.logger.debug( "Search for `{0}` not allowed by `patron_pid:{1}`".format( query_string, str(g.identity.id)) ) abort(403) try: search = search.query(query) except SyntaxError: current_app.logger.debug( "Failed parsing query: {0}".format(query_string), exc_info=True) raise InvalidQueryRESTError() search_index = search._index[0] search, urlkwargs = default_facets_factory(search, search_index) search, sortkwargs = default_sorter_factory(search, search_index) for key, value in sortkwargs.items(): urlkwargs.add(key, value) urlkwargs.add('q', query_string) return search, urlkwargs
def filter_by_patron_search_factory(self, search, query_parser=None): """Filter search queries to only show records for the logged in patron. If the logged in user has backoffice permissions do not filter by patron. :param self: REST view. :param search: Elastic search DSL search instance. :returns: Tuple with search instance and URL arguments. """ def _default_parser(qstr=None): """Return default parser that uses the Q() from elasticsearch_dsl.""" if qstr: return Q('query_string', query=qstr) return Q() from invenio_records_rest.facets import default_facets_factory from invenio_records_rest.sorter import default_sorter_factory query_string = request.values.get('q', '') if not current_user.is_authenticated: raise UnauthorizedSearchError(query_string) parser = query_parser or _default_parser query = parser(qstr=query_string) # if the logged in user in not librarian or admin, validate the query if has_request_context() and not backoffice_permission().allows(g.identity): # patron can find only his document requests if not query_string: # force query to be patron_pid:<logged in user> patron_pid_filter = 'patron_pid:{}'.format(g.identity.id) query = _default_parser(qstr=patron_pid_filter) else: # check for patron_pid query value match = re.match(r"patron_pid:(?P<pid>\d)", query_string) if match and match.group('pid') != str(g.identity.id): raise UnauthorizedSearchError(query_string, g.identity.id) try: search = search.query(query) except SyntaxError: raise SearchQueryError(query_string) search_index = search._index[0] search, urlkwargs = default_facets_factory(search, search_index) search, sortkwargs = default_sorter_factory(search, search_index) for key, value in sortkwargs.items(): urlkwargs.add(key, value) urlkwargs.add('q', query_string) return search, urlkwargs
def filter_by_patron(query_string): """Filter search results by patron_pid.""" if not current_user.is_authenticated: raise UnauthorizedSearchError(query_string) # if the logged in user is not librarian or admin, validate the query if has_request_context() and not backoffice_permission().allows( g.identity): # patron can find only his records if not query_string: # force query to be patron_pid:<logged in user> query_string = "patron_pid:{}".format(g.identity.id) else: # check for patron_pid query value match = re.match(r"patron_pid:(?P<pid>\d+)", query_string) if match and match.group("pid") != str(g.identity.id): raise UnauthorizedSearchError(query_string, g.identity.id) return query_string
def circulation_default_extension_max_count(loan): """Return a default extensions max count.""" # NOTE: If user is admin or librarian always have 1 extra extension count. if has_request_context() and backoffice_permission().allows(g.identity): return loan.get("extension_count", 0) + 1 return 3