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)
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 )
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)
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)
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)
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)
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)
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), ])
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)
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
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
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)
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)
def fake_environ(self, queue): return { "h.ws.identity": Identity(), "h.ws.registry": mock.sentinel.registry, "h.ws.streamer_work_queue": queue, }
def identity(factories): return Identity.from_models(user=factories.User.build(), auth_client=factories.AuthClient.build())
def with_auth_client(self, factories, pyramid_config): pyramid_config.testing_securitypolicy(identity=Identity.from_models( auth_client=factories.AuthClient()))
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
def identity(self, factories): return Identity.from_models(user=factories.User())
def identity(self, pyramid_config, factories): identity = Identity.from_models(auth_client=factories.AuthClient()) pyramid_config.testing_securitypolicy(identity=identity) return identity
def with_auth_client(auth_client, pyramid_config): pyramid_config.testing_securitypolicy(identity=Identity.from_models( auth_client=auth_client))