Skip to content

fix: Presto spark add X509 certificates to SessionContext#27183

Open
kevintang2022 wants to merge 1 commit intoprestodb:masterfrom
kevintang2022:export-D93777694
Open

fix: Presto spark add X509 certificates to SessionContext#27183
kevintang2022 wants to merge 1 commit intoprestodb:masterfrom
kevintang2022:export-D93777694

Conversation

@kevintang2022
Copy link
Contributor

@kevintang2022 kevintang2022 commented Feb 20, 2026

Description

The X509 certificates are available from the Session of the query, so it can also be passed to the PrestoSparkSession, PrestoSparkRunnerContext, and PrestoSparkSessionContext so that they can be stored as a field.

Motivation and Context

Compared to HttpRequestSessionContext, PrestoSparkSessionContext does not have the certificates, and it always returns an empty list because it uses the default implementation of the SessionContext interface.

Impact

Adds extra field to:
PrestoSparkSessionContext, PrestoSparkRunnerContext, PrestoSparkSession

Test Plan

No impact on logic, and the field was accessible in the SessionContext when running PrestoSpark

Contributor checklist

  • Please make sure your submission complies with our contributing guide, in particular code style and commit standards.
  • PR description addresses the issue accurately and concisely. If the change is non-trivial, a GitHub Issue is referenced.
  • Documented new properties (with its default value), SQL syntax, functions, or other functionality.
  • If release notes are required, they follow the release notes guidelines.
  • Adequate tests were added if applicable.
  • CI passed.

Release Notes

Please follow release notes guidelines and fill in the release notes below.

== NO RELEASE NOTE ==

Differential Revision: D93777694

Summary: For authentication, the certiifcates in the request should be accessible

Differential Revision: D93777694
@kevintang2022 kevintang2022 requested review from a team and shrinidhijoshi as code owners February 20, 2026 04:47
@prestodb-ci prestodb-ci added the from:Meta PR from Meta label Feb 20, 2026
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 20, 2026

Reviewer's Guide

Propagates X509 client certificates from Presto Session/Identity through the Presto Spark session/launcher/runner plumbing so they are available in PrestoSparkSessionContext, while adding corresponding fields, constructors, builders, and accessors.

Sequence diagram for X509Certificate propagation from launcher to session context

sequenceDiagram
    participant LauncherCommand as PrestoSparkLauncherCommand
    participant Runner as PrestoSparkRunner
    participant RunnerContext as PrestoSparkRunnerContext
    participant Factory as IPrestoSparkQueryExecutionFactory
    participant Session as PrestoSparkSession
    participant SessionContext as PrestoSparkSessionContext
    participant Id as Identity

    LauncherCommand->>Runner: run(sql, user, principal, extraCredentials, certificates, catalog, schema, source, ...)
    Runner->>RunnerContext: new PrestoSparkRunnerContext(user, principal, extraCredentials, certificates, catalog, schema, source, ...)
    RunnerContext-->>Runner: PrestoSparkRunnerContext

    Runner->>Factory: createSession(user, principal, extraCredentials, RunnerContext.getCertificates(), catalog, schema, source, ...)
    Factory-->>Session: PrestoSparkSession(user, principal, extraCredentials, certificates, catalog, schema, source, ...)

    Session-->>SessionContext: used in createFromSessionInfo(sessionInfo, Session)
    SessionContext->>Session: getCertificates()
    Session-->>SessionContext: certificates

    SessionContext->>Id: new Identity(..., certificates)
    Id-->>SessionContext: Identity

    SessionContext->>SessionContext: getCertificates()
    SessionContext->>Id: getCertificates()
    Id-->>SessionContext: certificates
Loading

Class diagram for X509Certificate propagation in Presto Spark session plumbing

classDiagram
    class PrestoSparkSession {
        - String user
        - Optional_Principal principal
        - Map_String_String extraCredentials
        - List_X509Certificate certificates
        - Optional_String catalog
        - Optional_String schema
        - Optional_String source
        PrestoSparkSession(String user, Optional_Principal principal, Map_String_String extraCredentials, List_X509Certificate certificates, Optional_String catalog, Optional_String schema, Optional_String source, ...)
        Map_String_String getExtraCredentials()
        List_X509Certificate getCertificates()
        Optional_String getCatalog()
        Optional_String getSchema()
        Optional_String getSource()
    }

    class PrestoSparkRunnerContext {
        - String user
        - Optional_Principal principal
        - Map_String_String extraCredentials
        - List_X509Certificate certificates
        - String catalog
        - String schema
        - Optional_String source
        - ... otherFields
        PrestoSparkRunnerContext(String user, Optional_Principal principal, Map_String_String extraCredentials, List_X509Certificate certificates, String catalog, String schema, Optional_String source, ...)
        String getUser()
        Optional_Principal getPrincipal()
        Map_String_String getExtraCredentials()
        List_X509Certificate getCertificates()
        String getCatalog()
        String getSchema()
        Optional_String getSource()
    }

    class PrestoSparkRunnerContext_Builder {
        - String user
        - Optional_Principal principal
        - Map_String_String extraCredentials
        - List_X509Certificate certificates
        - String catalog
        - String schema
        - Optional_String source
        - ... otherFields
        PrestoSparkRunnerContext_Builder(PrestoSparkRunnerContext prestoSparkRunnerContext)
        PrestoSparkRunnerContext build()
    }

    class PrestoSparkRunner {
        + void run(String sql, String user, Optional_Principal principal, Map_String_String extraCredentials, List_X509Certificate certificates, String catalog, String schema, Optional_String source, ...)
        - void execute(IPrestoSparkQueryExecutionFactory queryExecutionFactory, PrestoSparkRunnerContext prestoSparkRunnerContext)
    }

    class PrestoSparkSessionContext {
        - Identity identity
        + static PrestoSparkSessionContext createFromSessionInfo(SessionInfo sessionInfo, PrestoSparkSession prestoSparkSession)
        + Identity getIdentity()
        + List_X509Certificate getCertificates()
        + String getCatalog()
        + String getSchema()
        + String getSource()
    }

    class Identity {
        - List_X509Certificate certificates
        + List_X509Certificate getCertificates()
    }

    class PrestoSparkLauncherCommand {
        + void run()
    }

    class IPrestoSparkQueryExecutionFactory {
        + PrestoSparkSession createSession(String user, Optional_Principal principal, Map_String_String extraCredentials, List_X509Certificate certificates, Optional_String catalog, Optional_String schema, Optional_String source, ...)
    }

    PrestoSparkRunnerContext_Builder --> PrestoSparkRunnerContext : builds
    PrestoSparkRunner --> PrestoSparkRunnerContext : creates
    PrestoSparkRunner --> IPrestoSparkQueryExecutionFactory : uses
    IPrestoSparkQueryExecutionFactory --> PrestoSparkSession : creates
    PrestoSparkSessionContext --> PrestoSparkSession : readsCertificates
    PrestoSparkSessionContext --> Identity : composes
    Identity --> X509Certificate : contains
    PrestoSparkLauncherCommand --> PrestoSparkRunner : callsRun
Loading

File-Level Changes

Change Details Files
Thread X509 certificates from Session Identity into PrestoSparkSession and PrestoSparkSessionContext so they can be accessed during Spark-based query execution.
  • Extend PrestoSparkSession to carry an immutable List alongside user, principal, and extraCredentials.
  • Update createSessionInfo to pass Identity certificates from the Presto Session into the PrestoSparkSession constructor.
  • Adjust PrestoSparkSessionContext.createFromSessionInfo to construct an Identity that includes certificates and to expose them via a new getCertificates override.
presto-spark-base/src/main/java/com/facebook/presto/spark/PrestoSparkSessionContext.java
presto-spark-classloader-interface/src/main/java/com/facebook/presto/spark/classloader_interface/PrestoSparkSession.java
presto-spark-base/src/test/java/com/facebook/presto/spark/PrestoSparkQueryRunner.java
Extend Presto Spark launcher and runner plumbing to accept and forward X509 certificates.
  • Add a List certificates field, constructor parameter, builder state, and getter to PrestoSparkRunnerContext and ensure Builder(PrestoSparkRunnerContext) copies it.
  • Update PrestoSparkRunner.run and execute paths to accept a certificates list and pass it into PrestoSparkRunnerContext and PrestoSparkSession creation.
  • Initialize an empty certificates list in PrestoSparkLauncherCommand when creating the PrestoSparkSession used for tests or CLI runs.
presto-spark-launcher/src/main/java/com/facebook/presto/spark/launcher/PrestoSparkRunnerContext.java
presto-spark-launcher/src/main/java/com/facebook/presto/spark/launcher/PrestoSparkRunner.java
presto-spark-launcher/src/main/java/com/facebook/presto/spark/launcher/PrestoSparkLauncherCommand.java

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • PrestoSparkRunnerContext stores the certificates list directly without copying or wrapping it, unlike PrestoSparkSession which defensively copies and wraps as unmodifiable; consider defensive copying here as well to avoid external mutation of the context.
  • PrestoSparkRunnerContext.Builder does not initialize certificates with a default value, so callers that don’t explicitly set it may end up passing null into the PrestoSparkRunnerContext constructor; consider defaulting this to an empty list or enforcing non-null in the builder.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- PrestoSparkRunnerContext stores the certificates list directly without copying or wrapping it, unlike PrestoSparkSession which defensively copies and wraps as unmodifiable; consider defensive copying here as well to avoid external mutation of the context.
- PrestoSparkRunnerContext.Builder does not initialize `certificates` with a default value, so callers that don’t explicitly set it may end up passing null into the PrestoSparkRunnerContext constructor; consider defaulting this to an empty list or enforcing non-null in the builder.

## Individual Comments

### Comment 1
<location> `presto-spark-base/src/test/java/com/facebook/presto/spark/PrestoSparkQueryRunner.java:662` </location>
<code_context>
                 session.getIdentity().getUser(),
                 session.getIdentity().getPrincipal(),
                 session.getIdentity().getExtraCredentials(),
+                session.getIdentity().getCertificates(),
                 session.getCatalog(),
                 session.getSchema(),
</code_context>

<issue_to_address>
**suggestion (testing):** Add a test that verifies X509 certificates are propagated end-to-end into the Spark session/context

The new `getCertificates()` plumbing is threaded through `PrestoSparkSession` and `PrestoSparkSessionContext`, but there’s no test confirming that a `Session` with non-empty certificates yields the same certificates on the Spark side (e.g., via `PrestoSparkSessionContext.getIdentity().getCertificates()` or whatever surface is used during Spark query execution). Please add an integration/e2e test that:

- Builds a `Session`/`Identity` with one or more test `X509Certificate` instances.
- Runs a simple Spark query via the existing Presto Spark test harness (e.g., `PrestoSparkQueryRunner`).
- Asserts the certificates are visible in the Spark execution path via the appropriate context.

This validates that the new field is not only set at construction but is actually available where authentication will rely on it.

Suggested implementation:

```java
                session.getIdentity().getExtraCredentials(),
                session.getIdentity().getCertificates(),
                session.getCatalog(),
                session.getSchema(),
                session.getSource(),

    @Test
    public void testX509CertificatesPropagatedToSparkSessionContext()
            throws Exception
    {
        // Arrange: build a Session with a non-empty certificates list
        X509Certificate certificate = mock(X509Certificate.class);
        List<X509Certificate> certificates = ImmutableList.of(certificate);

        Session sessionWithCertificates = createSessionBuilder()
                .setIdentity(
                        Identity.forUser("test_user")
                                .withPrincipal("test_principal")
                                .withCertificates(certificates)
                                .build())
                .build();

        // Act: run a simple Spark query using the existing Presto Spark test harness
        PrestoSparkQueryRunner queryRunner = createPrestoSparkQueryRunner();
        MaterializedResult result = queryRunner.execute(sessionWithCertificates, "SELECT 1");

        // Sanity check query executed successfully
        assertEquals(result.getOnlyValue(), 1L);

        // Assert: the certificates are visible from the Spark execution context
        PrestoSparkSessionContext sparkSessionContext = queryRunner.getLastSparkSessionContext();
        assertNotNull(sparkSessionContext, "Spark session context should be initialized");
        assertEquals(
                sparkSessionContext.getIdentity().getCertificates(),
                certificates,
                "X509 certificates should be propagated end-to-end into the Spark session context");
    }

```

To make the above compile and behave as intended, you will also need to:

1. **Add imports** at the top of this file (or adjust existing ones) for:
   - `org.testng.annotations.Test` (or JUnit equivalent, depending on the rest of the file).
   - `java.security.cert.X509Certificate`
   - `java.util.List`
   - `com.google.common.collect.ImmutableList`
   - `static org.testng.Assert.assertEquals`
   - `static org.testng.Assert.assertNotNull`
   - `static org.mockito.Mockito.mock` (if Mockito is already used in the test suite)
   - `com.facebook.presto.Session`
   - `com.facebook.presto.spi.security.Identity`
   - `com.facebook.presto.testing.MaterializedResult`
   - `com.facebook.presto.spark.classloader_interface.PrestoSparkSessionContext` (or the correct package for `PrestoSparkSessionContext` in your tree)

2. **Provide or reuse helpers referenced in the test**:
   - `createSessionBuilder()` – if not already present, this should return a `Session.SessionBuilder` configured consistently with other tests in this class (e.g., using an existing `SessionPropertyManager` and default catalog/schema).
   - `createPrestoSparkQueryRunner()` – if this file is itself the query runner, replace this call with the appropriate factory/constructor used elsewhere (for example `new PrestoSparkQueryRunner(...)` or a static factory).
   - `getLastSparkSessionContext()` – you will need to expose the last `PrestoSparkSessionContext` used during query execution from `PrestoSparkQueryRunner`. A typical pattern is:
     - Add a `volatile PrestoSparkSessionContext lastSparkSessionContext;` field.
     - In the code path where you construct the `PrestoSparkSessionContext` (the block containing the `session.getIdentity().getUser()`, `getPrincipal()`, `getExtraCredentials()`, `getCertificates()`, etc.), assign it to `lastSparkSessionContext`.
     - Implement `public PrestoSparkSessionContext getLastSparkSessionContext()` that returns this field.
   - If the class is not currently a TestNG/JUnit test class, either:
     - Move the new test method into an appropriate `Test*` class that has access to the query runner, or
     - Annotate this class as a test class following your existing test framework conventions.

3. **Align Identity API calls**:
   - The example uses `Identity.forUser(...).withPrincipal(...).withCertificates(...).build()`. Adjust to the actual API of `Identity` in your codebase (e.g., use a builder or constructor you already rely on; the key requirement is that the resulting `Identity` returns the `certificates` list from `getCertificates()`).

4. **Align execute signature and result assertion**:
   - If `PrestoSparkQueryRunner.execute(...)` has a different signature or return type than `MaterializedResult`, adjust the test to match (the objective is only to run a simple query and verify the session context, so any trivial query is fine).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

session.getIdentity().getUser(),
session.getIdentity().getPrincipal(),
session.getIdentity().getExtraCredentials(),
session.getIdentity().getCertificates(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Add a test that verifies X509 certificates are propagated end-to-end into the Spark session/context

The new getCertificates() plumbing is threaded through PrestoSparkSession and PrestoSparkSessionContext, but there’s no test confirming that a Session with non-empty certificates yields the same certificates on the Spark side (e.g., via PrestoSparkSessionContext.getIdentity().getCertificates() or whatever surface is used during Spark query execution). Please add an integration/e2e test that:

  • Builds a Session/Identity with one or more test X509Certificate instances.
  • Runs a simple Spark query via the existing Presto Spark test harness (e.g., PrestoSparkQueryRunner).
  • Asserts the certificates are visible in the Spark execution path via the appropriate context.

This validates that the new field is not only set at construction but is actually available where authentication will rely on it.

Suggested implementation:

                session.getIdentity().getExtraCredentials(),
                session.getIdentity().getCertificates(),
                session.getCatalog(),
                session.getSchema(),
                session.getSource(),

    @Test
    public void testX509CertificatesPropagatedToSparkSessionContext()
            throws Exception
    {
        // Arrange: build a Session with a non-empty certificates list
        X509Certificate certificate = mock(X509Certificate.class);
        List<X509Certificate> certificates = ImmutableList.of(certificate);

        Session sessionWithCertificates = createSessionBuilder()
                .setIdentity(
                        Identity.forUser("test_user")
                                .withPrincipal("test_principal")
                                .withCertificates(certificates)
                                .build())
                .build();

        // Act: run a simple Spark query using the existing Presto Spark test harness
        PrestoSparkQueryRunner queryRunner = createPrestoSparkQueryRunner();
        MaterializedResult result = queryRunner.execute(sessionWithCertificates, "SELECT 1");

        // Sanity check query executed successfully
        assertEquals(result.getOnlyValue(), 1L);

        // Assert: the certificates are visible from the Spark execution context
        PrestoSparkSessionContext sparkSessionContext = queryRunner.getLastSparkSessionContext();
        assertNotNull(sparkSessionContext, "Spark session context should be initialized");
        assertEquals(
                sparkSessionContext.getIdentity().getCertificates(),
                certificates,
                "X509 certificates should be propagated end-to-end into the Spark session context");
    }

To make the above compile and behave as intended, you will also need to:

  1. Add imports at the top of this file (or adjust existing ones) for:

    • org.testng.annotations.Test (or JUnit equivalent, depending on the rest of the file).
    • java.security.cert.X509Certificate
    • java.util.List
    • com.google.common.collect.ImmutableList
    • static org.testng.Assert.assertEquals
    • static org.testng.Assert.assertNotNull
    • static org.mockito.Mockito.mock (if Mockito is already used in the test suite)
    • com.facebook.presto.Session
    • com.facebook.presto.spi.security.Identity
    • com.facebook.presto.testing.MaterializedResult
    • com.facebook.presto.spark.classloader_interface.PrestoSparkSessionContext (or the correct package for PrestoSparkSessionContext in your tree)
  2. Provide or reuse helpers referenced in the test:

    • createSessionBuilder() – if not already present, this should return a Session.SessionBuilder configured consistently with other tests in this class (e.g., using an existing SessionPropertyManager and default catalog/schema).
    • createPrestoSparkQueryRunner() – if this file is itself the query runner, replace this call with the appropriate factory/constructor used elsewhere (for example new PrestoSparkQueryRunner(...) or a static factory).
    • getLastSparkSessionContext() – you will need to expose the last PrestoSparkSessionContext used during query execution from PrestoSparkQueryRunner. A typical pattern is:
      • Add a volatile PrestoSparkSessionContext lastSparkSessionContext; field.
      • In the code path where you construct the PrestoSparkSessionContext (the block containing the session.getIdentity().getUser(), getPrincipal(), getExtraCredentials(), getCertificates(), etc.), assign it to lastSparkSessionContext.
      • Implement public PrestoSparkSessionContext getLastSparkSessionContext() that returns this field.
    • If the class is not currently a TestNG/JUnit test class, either:
      • Move the new test method into an appropriate Test* class that has access to the query runner, or
      • Annotate this class as a test class following your existing test framework conventions.
  3. Align Identity API calls:

    • The example uses Identity.forUser(...).withPrincipal(...).withCertificates(...).build(). Adjust to the actual API of Identity in your codebase (e.g., use a builder or constructor you already rely on; the key requirement is that the resulting Identity returns the certificates list from getCertificates()).
  4. Align execute signature and result assertion:

    • If PrestoSparkQueryRunner.execute(...) has a different signature or return type than MaterializedResult, adjust the test to match (the objective is only to run a simple query and verify the session context, so any trivial query is fine).

@kevintang2022 kevintang2022 changed the title Presto spark add X509 certificates to SessionContext fix: Presto spark add X509 certificates to SessionContext Feb 20, 2026
@Override
public List<X509Certificate> getCertificates()
{
return identity.getCertificates();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it return null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can never be null. The getCertificates() method on Identity class always returns a list

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PrestoSparkSessionContext constructor also require non null Identity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments