async def check_database() -> None: async with ComponentFactory.standalone() as factory: admin_service = factory.create_admin_service() expected = [Admin(username=u) for u in config.initial_admins] assert await admin_service.get_admins() == expected token_service = factory.create_token_service() bootstrap = TokenData.bootstrap_token() assert await token_service.list_tokens(bootstrap) == []
async def build_history( setup: SetupTest, ) -> List[TokenChangeHistoryEntry]: """Perform a bunch of token manipulations and return the history entries. Assume that all token manipulations generate the correct history entries, since that's tested in other tests. The only point of this function is to build enough history that we can make interesting paginated queries of it. """ token_service = setup.factory.create_token_service() user_info_one = TokenUserInfo(username="******") token_one = await token_service.create_session_token( user_info_one, scopes=["exec:test", "read:all", "user:token"], ip_address="192.0.2.3", ) token_data_one = await token_service.get_data(token_one) assert token_data_one await token_service.get_internal_token( token_data_one, "foo", scopes=["exec:test", "read:all"], ip_address="192.0.2.4", ) internal_token_one_bar = await token_service.get_internal_token( token_data_one, "bar", scopes=["read:all"], ip_address="192.0.2.3" ) token_data_internal_one_bar = await token_service.get_data( internal_token_one_bar ) assert token_data_internal_one_bar await token_service.get_internal_token( token_data_internal_one_bar, "baz", scopes=[], ip_address="10.10.10.10" ) notebook_token_one = await token_service.get_notebook_token( token_data_one, ip_address="198.51.100.5" ) token_data_notebook_one = await token_service.get_data(notebook_token_one) assert token_data_notebook_one await token_service.get_internal_token( token_data_notebook_one, "foo", scopes=["exec:test"], ip_address="10.10.10.20", ) user_info_two = TokenUserInfo(username="******") token_two = await token_service.create_session_token( user_info_two, scopes=["read:some", "user:token"], ip_address="192.0.2.20", ) token_data_two = await token_service.get_data(token_two) assert token_data_two user_token_two = await token_service.create_user_token( token_data_two, token_data_two.username, token_name="some token", scopes=["read:some", "user:token"], ip_address="192.0.2.20", ) token_data_user_two = await token_service.get_data(user_token_two) assert token_data_user_two await token_service.get_internal_token( token_data_user_two, "foo", scopes=["read:some"], ip_address="10.10.10.10", ) assert await token_service.modify_token( user_token_two.key, token_data_user_two, token_data_user_two.username, ip_address="192.0.2.20", token_name="happy token", ) request = AdminTokenRequest( username="******", token_type=TokenType.service, scopes=["admin:token"], ) service_token = await token_service.create_token_from_admin_request( request, TokenData.bootstrap_token(), ip_address="2001:db8:034a:ea78:4278:4562:6578:9876", ) service_token_data = await token_service.get_data(service_token) assert service_token_data assert await token_service.modify_token( user_token_two.key, service_token_data, ip_address="2001:db8:034a:ea78:4278:4562:6578:9876", scopes=["admin:token", "read:all"], ) assert await token_service.modify_token( user_token_two.key, service_token_data, ip_address="2001:db8:034a:ea78:4278:4562:6578:af42", token_name="other name", expires=current_datetime() + timedelta(days=30), scopes=["read:all"], ) assert await token_service.delete_token( token_one.key, service_token_data, username=token_data_one.username, ip_address="2001:db8:034a:ea78:4278:4562:6578:9876", ) # Spread out the timestamps so that we can test date range queries. Every # other entry has the same timestamp as the previous entry to test that # queries handle entries with the same timestamp. entries = ( setup.session.query(TokenChangeHistory) .order_by(TokenChangeHistory.id) .all() ) event_time = current_datetime() - timedelta(seconds=len(entries) * 5) with setup.transaction(): for i, entry in enumerate(entries): entry.event_time = event_time if i % 2 != 0: event_time += timedelta(seconds=5) history = token_service.get_change_history(service_token_data) assert history.count == 20 assert len(history.entries) == 20 return history.entries
async def authenticate(self, context: RequestContext, x_csrf_token: Optional[str] = None) -> TokenData: """Authenticate the request. Always check the user's cookie-based session first before checking the ``Authorization`` header because some applications (JupyterHub, for instance) may use the ``Authorization`` header for their own purposes. If the request was authenticated via a browser cookie rather than a provided ``Authorization`` header, and the method was something other than ``GET`` or ``OPTIONS``, require and verify the CSRF header as well. Parameters ---------- context : `gafaelfawr.dependencies.context.RequestContext` The request context. x_csrf_token : `str`, optional The value of the ``X-CSRF-Token`` header, if provided. Returns ------- data : `gafaelfawr.models.token.TokenData` The data associated with the verified token. Raises ------ fastapi.HTTPException If authentication is not provided or is not valid. """ token = context.state.token if token: context.rebind_logger(token_source="cookie") self._verify_csrf(context, x_csrf_token) elif not self.require_session: try: token_str = parse_authorization(context) if token_str: token = Token.from_str(token_str) except (InvalidRequestError, InvalidTokenError) as e: raise generate_challenge(context, self.auth_type, e) if not token: raise self._redirect_or_error(context) if self.allow_bootstrap_token: if token == context.config.bootstrap_token: bootstrap_data = TokenData.bootstrap_token() context.rebind_logger( token="<bootstrap>", user="******", scope=" ".join(sorted(bootstrap_data.scopes)), ) context.logger.info("Authenticated with bootstrap token") return bootstrap_data token_service = context.factory.create_token_service() data = await token_service.get_data(token) if not data: if context.state.token: raise self._redirect_or_error(context) else: exc = InvalidTokenError("Token is not valid") raise generate_challenge(context, self.auth_type, exc) context.rebind_logger( token=token.key, user=data.username, scope=" ".join(sorted(data.scopes)), ) if self.require_scope and self.require_scope not in data.scopes: msg = f"Token does not have required scope {self.require_scope}" context.logger.info("Permission denied", error=msg) raise PermissionDeniedError(msg) return data