Skip to content

Fix Trino connector progress/cancel/SSL and CSRF token staleness on login - #4358

Closed
scott2112 wants to merge 5 commits into
cloudera:masterfrom
scott2112:hue-trino-oidc-fixes
Closed

scott2112 wants to merge 5 commits into
cloudera:masterfrom
scott2112:hue-trino-oidc-fixes

Conversation

@scott2112

@scott2112 scott2112 commented Jul 31, 2026 •

Copy link
Copy Markdown

What changes were proposed in this pull request?

  • trino.py: check_status() no longer reports RUNNING queries as 'available'. Previously hardcoded status to 'available' for Trino's RUNNING state with a "# need to verify" comment left in place, causing the UI progress bar to show the query as finished seconds after it moved past QUEUED, while it was still actually executing in Trino.

  • trino.py: add a cancel() method. TrinoApi had no cancel(), unlike every other connector (sql_alchemy.py, hiveserver2.py, jdbc.py), so clicking Cancel on a running query raised 'TrinoApi' object has no attribute 'cancel'. Delegates to the existing close_statement(), which already does the right DELETE on the query's next_uri.

  • trino.py: support a self-signed/internal CA cert for HTTPS connections via a new ssl_cert_path interpreter option. TrinoRequest was never passed a verify argument, so certificate validation always used the default system/certifi CA bundle with no way to trust an internal CA, causing "certificate verify failed: unable to get local issuer certificate". The new option merges the custom cert into a combined bundle with the certifi defaults (written once per process) rather than replacing the trust store outright, so other outbound HTTPS calls Hue makes still validate against public CAs normally.

  • trino.py: check_status() now surfaces any polled row data in its response instead of dropping it, and always returns the correctly advanced next_uri.

  • api.py: _check_status() persists that advanced next_uri and any polled row data into the snippet's handle, so fetch_result() can recover it even from a different worker process.

  • snippet.js: the legacy KO editor only copied next_uri out of a check_status response in the running/waiting branch, never in the available branch that triggers fetchResult() -- fixed to apply unconditionally, including seeding any row data delivered alongside that poll.

  • global_js_constants.mako: use django.middleware.csrf.get_token(request) instead of reading request.COOKIES.get('csrftoken') directly to seed window.CSRF_TOKEN. The old approach embedded whatever CSRF cookie arrived with the request, which is stale if the same request rotates the CSRF token (e.g. via login()) later in its own processing - every subsequent AJAX POST would then send a token that no longer matched the session, failing CSRF validation and leaving the UI on a blank screen after login.

  • settings.py: move CsrfViewMiddleware to run immediately after SessionMiddleware, before AuthenticationMiddleware/SpnegoMiddleware/ HueRemoteUserMiddleware (matching Django's own recommended default ordering). Those middlewares can call login()/rotate_token() inline while handling a request (e.g. RemoteUserDjangoBackend auto-login from a trusted proxy header). With CsrfViewMiddleware positioned after them, its process_request() would unconditionally reset the just-rotated CSRF secret back to the stale incoming cookie value, reintroducing the same token-mismatch/blank-screen symptom independent of the template fix above.

How was this patch tested?

  • Verified via pytest against a full build (all 23 tests pass, including the 17 pre-existing ones - make test/hue test uses stale nose-era flags with no corresponding custom Django test command, while the repo's root conftest.py confirms pytest is the actual intended runner).

  • test_check_status_running: confirms RUNNING is now reported as 'running' instead of 'available', matching the progress-bar fix.

  • test_cancel: confirms cancel() delegates to close_statement().

  • test_init_verify_defaults_to_true_without_ssl_cert_path / test_init_verify_uses_combined_ca_bundle_with_ssl_cert_path: confirm the verify kwarg passed to TrinoRequest in both the default and ssl_cert_path-configured cases.

  • test_get_combined_ca_bundle_merges_certifi_and_extra_cert / test_get_combined_ca_bundle_is_cached_after_first_call: confirm the CA bundle merge and once-per-process caching behavior.

Please review Hue Contributing Guide before opening a pull request.

@scott2112
scott2112 force-pushed the hue-trino-oidc-fixes branch from f35fdfb to 26a6c67 Compare September 1, 2026 22:57
@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open 45 days with no activity and is not labeled "Prevent stale". Remove "stale" label or comment or this will be closed in 10 days.

@github-actions github-actions Bot added the Stale label Oct 17, 2026
scott2112 and others added 5 commits October 26, 2026 09:58
…ogin

- trino.py: check_status() no longer reports RUNNING queries as 'available'.
  Previously hardcoded status to 'available' for Trino's RUNNING state with a
  "# need to verify" comment left in place, causing the UI progress bar to show
  the query as finished seconds after it moved past QUEUED, while it was still
  actually executing in Trino.

- trino.py: add a cancel() method. TrinoApi had no cancel(), unlike every other
  connector (sql_alchemy.py, hiveserver2.py, jdbc.py), so clicking Cancel on a
  running query raised 'TrinoApi' object has no attribute 'cancel'. Delegates
  to the existing close_statement(), which already does the right DELETE on
  the query's next_uri.

- trino.py: support a self-signed/internal CA cert for HTTPS connections via a
  new ssl_cert_path interpreter option. TrinoRequest was never passed a verify
  argument, so certificate validation always used the default system/certifi
  CA bundle with no way to trust an internal CA, causing "certificate verify
  failed: unable to get local issuer certificate". The new option merges the
  custom cert into a combined bundle with the certifi defaults (written once
  per process) rather than replacing the trust store outright, so other
  outbound HTTPS calls Hue makes still validate against public CAs normally.

- global_js_constants.mako: use django.middleware.csrf.get_token(request)
  instead of reading request.COOKIES.get('csrftoken') directly to seed
  window.CSRF_TOKEN. The old approach embedded whatever CSRF cookie arrived
  with the request, which is stale if the same request rotates the CSRF
  token (e.g. via login()) later in its own processing - every subsequent
  AJAX POST would then send a token that no longer matched the session,
  failing CSRF validation and leaving the UI on a blank screen after login.

- settings.py: move CsrfViewMiddleware to run immediately after
  SessionMiddleware, before AuthenticationMiddleware/SpnegoMiddleware/
  HueRemoteUserMiddleware (matching Django's own recommended default
  ordering). Those middlewares can call login()/rotate_token() inline while
  handling a request (e.g. RemoteUserDjangoBackend auto-login from a
  trusted proxy header). With CsrfViewMiddleware positioned after them,
  its process_request() would unconditionally reset the just-rotated CSRF
  secret back to the stale incoming cookie value, reintroducing the same
  token-mismatch/blank-screen symptom independent of the template fix above.
Verified via pytest against a full build (all 23 tests pass, including
the 17 pre-existing ones - make test/hue test uses stale nose-era flags
with no corresponding custom Django test command, while the repo's root
conftest.py confirms pytest is the actual intended runner).

- test_check_status_running: confirms RUNNING is now reported as 'running'
  instead of 'available', matching the progress-bar fix.
- test_cancel: confirms cancel() delegates to close_statement().
- test_init_verify_defaults_to_true_without_ssl_cert_path /
  test_init_verify_uses_combined_ca_bundle_with_ssl_cert_path: confirm the
  verify kwarg passed to TrinoRequest in both the default and
  ssl_cert_path-configured cases.
- test_get_combined_ca_bundle_merges_certifi_and_extra_cert /
  test_get_combined_ca_bundle_is_cached_after_first_call: confirm the CA
  bundle merge and once-per-process caching behavior.
…h_result

check_status() polls Trino's next_uri to learn query state, but that GET
advances Trino's pagination cursor and can consume a page of row data along
with the status check. That data and the advanced next_uri were being
discarded, so a later poll or fetch_result() call could re-request an
already-consumed URI and get back an unpredictable subset of the real rows.

- trino.py: check_status() now surfaces any polled row data in its response
  instead of dropping it, and always returns the correctly advanced next_uri.
- api.py: _check_status() persists that advanced next_uri and any polled row
  data into the snippet's handle, so fetch_result() can recover it even from
  a different worker process.
- snippet.js: the legacy KO editor only copied next_uri out of a check_status
  response in the running/waiting branch, never in the available branch that
  triggers fetchResult() -- fixed to apply unconditionally, including seeding
  any row data delivered alongside that poll.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Downloads always re-execute and poll to completion server-side via
TrinoExecutionWrapper, independent of the browser-facing check_status/
fetch_result_data flow fixed earlier. TrinoExecutionWrapper had its own
duplicate _until_available() that shadowed the already-fixed base class
method entirely, and was worse: it explicitly reverted next_uri back to
the stale, already-consumed value right before returning, guaranteeing
fetch_result() would re-GET an already-consumed Trino continuation URI
and get back an unpredictable subset of the real rows.

- trino.py: remove TrinoExecutionWrapper's duplicate _until_available()
  so it inherits the base class's fixed implementation. fetch_result()
  now seeds columns from the handle (needed when next_uri already arrived
  as None) and preserves earlier-established columns across a later,
  columns-less page.
- base_tests.py: add coverage for the shared _until_available() persistence
  logic now exercised by every connector that goes through it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
If check_status() polling accumulates more than 100 rows into a query's
seed before it reaches its terminal (next_uri=None) state, fetch_result()
would return only the first 100 and report has_more=False -- permanently
losing the rest, since there's no next_uri left to recover them from once
they're already in the seed.

row_count is now used purely as an offset into the (never mutated or
discarded) seed, so a seed bigger than one page is correctly paged across
successive fetch_result() calls, matching how callers already write
row_count/rows_remaining/next_uri back into the handle between calls.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@scott2112
scott2112 force-pushed the hue-trino-oidc-fixes branch from 26a6c67 to aaacd6f Compare October 26, 2026 17:05
@Harshg999

Copy link
Copy Markdown
Collaborator

Hi, thanks for your contribution! I've fixed the basic Trino support as part of #4393. This should part of next upcoming 4.12 release.

@Harshg999 Harshg999 closed this Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants