Exemple #1
0
 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
Exemple #3
0
    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