Skip to content

[2.0] fix(security): restrict service connection reads to bots and editors - #36265

Closed
gh-bot-collate[bot] wants to merge 1 commit into
2.0from
backport/2.0/pr-36219
Closed

gh-bot-collate[bot] wants to merge 1 commit into
2.0from
backport/2.0/pr-36219

Conversation

@gh-bot-collate

@gh-bot-collate gh-bot-collate Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Automated backport of #36219 to 2.0.

Source PR #36219
Author @mohityadav766
Merge commit 4934556c77bbfcbe41845b02f5bb7ee4c3e03328
Method Conflicts resolved by Claude — worth a read of the diff.

Resolution notes

Backport notes: #36219 → 2.0

Source PR: fix(security): restrict service connection reads to bots and editors (#36219)
Merge commit: 4934556c77bbfcbe41845b02f5bb7ee4c3e03328

What merged cleanly

git cherry-pick auto-merged the production code and most tests without
conflict:

  • ServiceEntityResource.java — decryptOrNullify now branches on bot vs.
    EDIT_ALL vs. everyone else, via the new canEditConnection helper.
  • MetadataServiceResource.java — the built-in OpenMetadata service no
    longer stores the Elasticsearch sink or the ingestion-bot JWT.
  • EntityMasker.java — OpenMetadataJWTClientConfig removed from the
    "do not mask" exemption list, so its jwtToken is masked for non-bot
    readers.
  • Migration scripts (MySQL/Postgres 2.0.7/postDataMigrationSQLScript.sql)
    scrubbing the ES sink / bot JWT from existing OpenMetadata service rows
    and their version history.
  • docs/decisions/2026-10-26-service-connection-visibility-follows-edit-permission.md
    (new file).
  • MessagingServiceResourceIT.java — rewritten assertions (view-only users
    now get null connection instead of a masked one).

Conflicts resolved (import-only, both files)

Both unmerged files had only import-block conflicts; the actual test
bodies the PR added had already auto-merged cleanly on top of the 2.0
content. In each case I kept the imports the backported test code actually
uses and dropped the HEAD/main side imports that belong to tests/code
not present on 2.0:

  • openmetadata-integration-tests/.../MetadataServiceResourceIT.java
    Kept org.openmetadata.schema.utils.JsonUtils (used by the two new tests
    this PR adds: serviceConnection_maskedForEditor_nullForViewer_fullForBot
    and builtInOpenMetadataService_hasNoEsSinkOrStoredBotToken). Dropped
    org.openmetadata.sdk.client.OpenMetadataClient — on main that import is
    needed by a putCreateOrUpdate helper and other tests added by later,
    unrelated commits that don't exist on 2.0; it would be unused here.

  • openmetadata-service/.../masker/PasswordEntityMaskerTest.java
    Kept org.openmetadata.schema.services.connections.metadata.OpenMetadataConnection
    (used by the new testOpenMetadataServerJwtTokenIsMaskedAndRestored test
    this PR adds). Dropped the microsoftFabric, drive/sftp, and mcp
    imports — those belong to other connector test methods added on main by
    unrelated commits and aren't present in this file on 2.0.

What a reviewer should double check

  • I verified by inspection (not by running mvn compile, since this
    environment doesn't have the sandbox for a full build) that every API the
    ported code references exists on 2.0: Authorizer.getPermission(ctx, user, ResourceContextInterface), EntityResource.getResourceContextById,
    MetadataOperation.EDIT_ALL, Permission.Access.ALLOW,
    OpenMetadataConnectionBuilder#withSecurityConfig. Worth a real build/test
    run before merging.
  • The migration lands in 2.0.7/postDataMigrationSQLScript.sql, which was an
    empty placeholder on 2.0 — confirm 2.0.7 is still the correct
    next-release migration bucket at merge time.
  • Please run mvn spotless:apply — the auto-merged decryptOrNullify has an
    awkwardly line-wrapped comment that formatting will clean up.

Opened from openmetadata-collate, which holds the credentials needed to open a PR here. It merges itself once this branch's required checks pass — no review needed. If a check fails it stays open and the author of #36219 is asked to look.

resolver run

RetriggerView in GreptileConfidence Score: 4/5

No runtime blocker was established, but the explicit repository style requirements must be satisfied before merging.

Findings

  1. P2 Viewer branch returns early ▶
  2. P2 Unchanged parameters omit final ▶
  3. P2 Delegated editors lack test coverage ▶

Summary

Service connections now follow the caller’s edit permission:

  • Service connections are visible only to bots and editors.
  • The built-in OpenMetadata service no longer stores its search sink or bot token.

The built-in OpenMetadata service no longer stores the search sink or bot JWT. Both database migrations remove those fields from current rows and stored versions. JWT masking gains a round-trip test.

No concrete runtime regression was established. Two explicit repository style requirements need fixes, and delegated-editor coverage should be added.

Acknowledged by gh-bot-collate[bot]: Java formatting was not run; the PR description requests mvn spotless:apply.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Service response with a connection] --> B{Bot caller?}
  B -->|Yes| C[Return decrypted connection]
  B -->|No| D{EDIT_ALL allowed?}
  D -->|Yes| E[Return masked connection]
  D -->|No| F[Return null connection]
Loading

Reviews (1) · Last reviewed commit: "fix(security): restrict service connecti..." · Reviewed by Greptile

…36219)

* fix(security): restrict service connection reads to bots and editors

Return a service connection in full only to bots, masked to callers who can edit the
service (admins and explicitly delegated operators with EDIT_ALL), and not at all to
anyone else. The built-in OpenMetadata service no longer stores the Elasticsearch sink
or the ingestion-bot JWT (ingestion builds its own securityConfig on the fly and
reindexing reads the search config from the application config); it keeps only
host/port/auth provider. The server-connection JWT is masked for non-bot readers, and a
migration drops those fields from existing metadata service rows and their version
history. The decision is recorded in docs/decisions.

* test: align messaging connection visibility with edit permissions

(cherry picked from commit 4934556)
@gh-bot-collate
gh-bot-collate Bot requested a review from a team as a code owner October 26, 2026 14:16
@gh-bot-collate
gh-bot-collate Bot requested review from manerow and removed request for a team October 26, 2026 14:16
@gh-bot-collate gh-bot-collate Bot added the safe to test Add this label to run secure Github workflows on PRs label Oct 26, 2026
@gh-bot-collate
gh-bot-collate Bot enabled auto-merge (squash) October 26, 2026 14:16
} else if (canEditConnection(securityContext, service)) {
service.getConnection().setConfig(retrieveServiceConnectionConfig(service, true));
} else {
return nullifyConnection(service);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Viewer branch returns early

decryptOrNullify now returns inside the viewer branch as well as at the end. The repository guide requires one return statement per method, placed at the end. Assign the result of nullifyConnection(service) to service, then use the existing trailing return. This requirement must be satisfied before merging.

Suggested change
return nullifyConnection(service);
service = nullifyConnection(service);

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

return service;
}

private boolean canEditConnection(SecurityContext securityContext, T service) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Unchanged parameters omit final

canEditConnection adds two parameters without final, although neither changes. The repository guide requires final on unchanged parameters. Add it to both parameters and to the new TestNamespace ns parameter in serviceConnection_maskedForEditor_nullForViewer_fullForBot. This requirement must be satisfied before merging.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +261 to +266
// An editor (admin holds EDIT_ALL) gets the connection with the password masked, not cleartext.
final MetadataService asEditor = SdkClients.adminClient().metadataServices().getByName(fqn);
assertNotNull(asEditor.getConnection());
final AmundsenConnection editorConn =
JsonUtils.convertValue(asEditor.getConnection().getConfig(), AmundsenConnection.class);
assertEquals(MASKED, editorConn.getPassword());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Delegated editors lack test coverage

The editor check uses only an administrator. Administrators bypass normal policy checks, so this test does not cover the main promise of canEditConnection: preserving access for non-admin owners and delegated editors. A change that hides their connections could still pass this test.

Add a non-admin caller with a conditional EDIT_ALL grant and verify a masked connection. Also check that a caller with only field-scoped edits receives no connection.

Knowledge Base Used: Platform security and governance

@sonarqubecloud

Copy link
Copy Markdown

This branch was previously deployed

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

Labels

safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant