Пример #1
0
    def test_identify_without_forwarded_user(self, pyramid_request,
                                             auth_client):
        pyramid_request.headers["X-Forwarded-User"] = None

        identity = AuthClientPolicy().identity(pyramid_request)

        assert identity == Identity.from_models(auth_client=auth_client)
Пример #2
0
    def test_identity(self, pyramid_request, auth_cookie_service):
        identity = CookiePolicy().identity(pyramid_request)

        auth_cookie_service.verify_cookie.assert_called_once()
        assert identity == Identity.from_models(
            user=auth_cookie_service.verify_cookie.return_value
        )
Пример #3
0
    def _load_identity(self, request):
        token_svc = request.find_service(name="auth_token")
        token_str = None

        # We only ever want to read the access token from GET parameters for
        # websocket requests
        if self._is_ws_request(request):
            token_str = request.GET.get("access_token", None)

        # We currently only accept a real bearer token in our API calls, but
        # it's conceivable the WS spec will one day change to accept this:
        # See: https://github.com/whatwg/html/issues/3062
        if token_str is None:
            token_str = token_svc.get_bearer_token(request)

        if token_str is None:
            return None

        token = token_svc.validate(token_str)
        if token is None:
            return None

        user = request.find_service(name="user").fetch(token.userid)
        if user is None:
            return None

        return Identity.from_models(user=user)
Пример #4
0
    def identity(self, request):
        self._add_vary_by_cookie(request)

        user = request.find_service(AuthCookieService).verify_cookie()
        if not user:
            return None

        return Identity.from_models(user=user)
Пример #5
0
    def test_identity(self, pyramid_request, auth_client, user_service):
        pyramid_request.headers["X-Forwarded-User"] = sentinel.forwarded_user

        identity = AuthClientPolicy().identity(pyramid_request)

        user_service.fetch.assert_called_once_with(sentinel.forwarded_user)
        assert identity == Identity.from_models(
            auth_client=auth_client, user=user_service.fetch.return_value)
Пример #6
0
    def test_identity(self, pyramid_request, user_service):
        pyramid_request.environ[
            "HTTP_X_FORWARDED_USER"] = sentinel.forwarded_user

        identity = RemoteUserPolicy().identity(pyramid_request)

        user_service.fetch.assert_called_once_with(sentinel.forwarded_user)
        assert identity == Identity.from_models(
            user=user_service.fetch.return_value)
Пример #7
0
    def identity(self, request):
        user_id = request.environ.get("HTTP_X_FORWARDED_USER")
        if not user_id:
            return None

        user = request.find_service(name="user").fetch(user_id)
        if user is None:
            return None

        return Identity.from_models(user=user)
Пример #8
0
    def test_it_records_socket_metrics(self, generate_metrics, sockets):
        sockets[0].identity = Identity()

        metrics = generate_metrics()

        assert list(metrics) == Any.list.containing([
            ("Custom/WebSocket/Connections/Active", 3),
            ("Custom/WebSocket/Connections/Authenticated", 1),
            ("Custom/WebSocket/Connections/Anonymous", 2),
        ])
Пример #9
0
    def test_identity(self, pyramid_request, auth_token_service, user_service):
        identity = BearerTokenPolicy().identity(pyramid_request)

        auth_token_service.get_bearer_token.assert_called_once_with(pyramid_request)
        auth_token_service.validate.assert_called_once_with(
            auth_token_service.get_bearer_token.return_value
        )
        user_service.fetch.assert_called_once_with(
            auth_token_service.validate.return_value.userid
        )
        assert identity == Identity.from_models(user=user_service.fetch.return_value)
Пример #10
0
    def present_for_user(self, annotation: Annotation, user: User):
        """
        Get the JSON presentation of an annotation for a particular user.

        This representation includes extra data the specific user is privy to
        and also hides moderated content from users who should not see it.

        :param annotation: Annotation to present
        :param user: User that the annotation is being presented to
        :return: A dict suitable for JSON serialisation
        """

        # Get the basic version which isn't user specific
        model = self.present(annotation)

        # The flagged value depends on whether this particular user has flagged
        model["flagged"] = self._flag_service.flagged(user=user, annotation=annotation)

        # Only moderators see the full flag count
        user_is_moderator = identity_permits(
            identity=Identity.from_models(user=user),
            context=AnnotationContext(annotation),
            permission=Permission.Annotation.MODERATE,
        )
        if user_is_moderator:
            model["moderation"] = {
                "flagCount": self._flag_service.flag_count(annotation)
            }

        # The hidden value depends on whether you are the author
        user_is_author = user and user.userid == annotation.userid
        if user_is_author or not annotation.is_hidden:
            model["hidden"] = False
        else:
            model["hidden"] = True

            # Non moderators have bad content hidden from them
            if not user_is_moderator:
                model.update({"text": "", "tags": []})

        return model
Пример #11
0
    def socket(self):
        socket = WebSocket(
            sock=None,
            environ={
                "h.ws.identity": Identity(),
                "h.ws.effective_principals":
                [security.Everyone, "group:__world__"],
                "h.ws.streamer_work_queue": None,
            },
        )

        # We need to fake out the send reply function so it doesn't try and
        # actually do it. Using a mock here is _very_ slow in the numbers we are
        # doing
        def fake_send(_reply):
            fake_send.count += 1

        fake_send.count = 0
        socket.send_json = fake_send

        return socket
Пример #12
0
    def test_it(self, user, group, annotation):
        # We aren't going to go bonkers here, but a couple of tests to show
        # this actually holds together. This isn't really to inform us of any
        # particular failure, but just give us sensitivity if this doesn't work
        # at all when you hook it together for real.

        identity = Identity.from_models(user=user)
        anno_context = AnnotationContext(annotation=annotation)

        # A user can delete their own annotation
        assert identity_permits(identity, anno_context, Permission.Annotation.DELETE)

        # Once a user is the creator of a group they can moderate
        assert not identity_permits(
            identity, anno_context, Permission.Annotation.MODERATE
        )
        group.creator = user
        assert identity_permits(identity, anno_context, Permission.Annotation.MODERATE)

        # Once a user is an admin they can do admin things
        assert not identity_permits(identity, None, Permission.AdminPage.HIGH_RISK)
        identity.user.admin = True
        assert identity_permits(identity, None, Permission.AdminPage.HIGH_RISK)
Пример #13
0
class AuthClientPolicy(IdentityBasedPolicy):
    """
    An authentication policy for registered AuthClients.

    Auth clients must be registered with grant type `client_credentials` and
    will need to perform basic HTTP auth with their username and password set
    to their auth client id, and secret.

    A client can also pass an `X-Forwarded-User` header with the userid to
    act as user in their authority. This will create a `request.user` and will
    look like a normal login. This user will have an additional
    principal, `client:{client_id}@{authority}` which lets you tell it apart
    from regular users.
    """

    #: List of route name-method combinations that should
    #: allow AuthClient authentication
    API_WHITELIST = [
        ("api.groups", "POST"),
        ("api.group", "PATCH"),
        ("api.group", "GET"),
        ("api.group_upsert", "PUT"),
        ("api.group_member", "POST"),
        ("api.users", "POST"),
        ("api.user_read", "GET"),
        ("api.user", "PATCH"),
        ("api.bulk", "POST"),
    ]

    @classmethod
    def handles(cls, request):
        """Get whether this policy should accept this request."""

        if request.matched_route:
            return (
                request.matched_route.name,
                request.method,
            ) in cls.API_WHITELIST
        return False

    def identity(self, request):
        """
        Get an Identity object for valid credentials.

        :param request: Pyramid request to inspect
        :returns: An `Identity` object if the login is authenticated or None
        """
        # Credentials are required
        auth_client = self._get_auth_client(request)
        if not auth_client:
            return None

        user = None
        if forwarded_userid := request.headers.get("X-Forwarded-User", None):
            # If we have a forwarded user it must be valid
            try:
                user = request.find_service(
                    name="user").fetch(forwarded_userid)
            except InvalidUserId:
                return None

            # If you forward a user it must exist and match your authority
            if not user or user.authority != auth_client.authority:
                return None

        return Identity.from_models(auth_client=auth_client, user=user)
Пример #14
0
 def fake_environ(self, queue):
     return {
         "h.ws.identity": Identity(),
         "h.ws.registry": mock.sentinel.registry,
         "h.ws.streamer_work_queue": queue,
     }
Пример #15
0
def identity(factories):
    return Identity.from_models(user=factories.User.build(),
                                auth_client=factories.AuthClient.build())
Пример #16
0
 def with_auth_client(self, factories, pyramid_config):
     pyramid_config.testing_securitypolicy(identity=Identity.from_models(
         auth_client=factories.AuthClient()))
Пример #17
0
def socket(factories):
    socket = create_autospec(WebSocket, instance=True)
    socket.effective_principals = [Everyone, "group:__world__"]
    socket.identity = Identity.from_models(user=factories.User())

    return socket
Пример #18
0
 def identity(self, factories):
     return Identity.from_models(user=factories.User())
Пример #19
0
    def identity(self, pyramid_config, factories):
        identity = Identity.from_models(auth_client=factories.AuthClient())

        pyramid_config.testing_securitypolicy(identity=identity)

        return identity
Пример #20
0
def with_auth_client(auth_client, pyramid_config):
    pyramid_config.testing_securitypolicy(identity=Identity.from_models(
        auth_client=auth_client))