def needs_scope(*scopes):
    """Decorator to restrict access to users or services with the required scope"""

    for scope in scopes:
        if scope not in scope_definitions:
            raise ValueError(f"Scope {scope} is not a valid scope")

    def scope_decorator(func):
        @functools.wraps(func)
        def _auth_func(self, *args, **kwargs):
            if not self.current_user:
                # not authenticated at all, fail with more generic message
                # this is the most likely permission error - missing or mis-specified credentials,
                # don't indicate that they have insufficient permissions.
                raise web.HTTPError(
                    403,
                    "Missing or invalid credentials.",
                )

            sig = inspect.signature(func)
            bound_sig = sig.bind(self, *args, **kwargs)
            bound_sig.apply_defaults()
            # Load scopes in case they haven't been loaded yet
            if not hasattr(self, 'expanded_scopes'):
                self.expanded_scopes = {}
                self.parsed_scopes = {}

            try:
                end_point = self.request.path
            except AttributeError:
                end_point = self.__name__

            s_kwargs = {}
            for resource in {'user', 'server', 'group', 'service'}:
                resource_name = resource + '_name'
                if resource_name in bound_sig.arguments:
                    resource_value = bound_sig.arguments[resource_name]
                    s_kwargs[resource] = resource_value

            if "server" in s_kwargs:
                # merge user_name, server_name into server=user/server
                if "user" not in s_kwargs:
                    raise ValueError(
                        "Cannot filter on 'server_name' without 'user_name'"
                    )
                s_kwargs["server"] = f"{s_kwargs['user']}/{s_kwargs['server']}"
                s_kwargs.pop("user")
            if len(s_kwargs) > 1:
                raise ValueError(
                    f"Cannot filter on more than one field, got {s_kwargs}"
                )
            elif s_kwargs:
                filter_, filter_value = next(iter(s_kwargs.items()))
            else:
                filter_ = filter_value = None

            for scope in scopes:
                if filter_ is not None:
                    scope = f"{scope}!{filter_}={filter_value}"
                app_log.debug("Checking access to %s via scope %s", end_point, scope)
                has_access = has_scope(
                    scope,
                    self.parsed_scopes,
                    post_filter=filter_ is None,
                    db=self.db,
                )
                if has_access:
                    return func(self, *args, **kwargs)
            app_log.warning(
                "Not authorizing access to %s. Requires any of [%s] on %s, not derived from scopes [%s]",
                end_point,
                ", ".join(scopes),
                "*" if filter_ is None else f"{filter_}={filter_value}",
                ", ".join(self.expanded_scopes),
            )
            if filter_ and any(scope in self.parsed_scopes for scope in scopes):
                # not allowed due do filtered access,
                # same error for nonexistence as missing permission
                raise web.HTTPError(
                    404, "No access to resources or resources not found"
                )
            raise web.HTTPError(
                403,
                "Action is not authorized with current scopes;"
                f" requires any of [{', '.join(scopes)}]",
            )

        return _auth_func

    return scope_decorator
Decorator to restrict access to users or services with the required scope
jupyterhub/scopes.py