fix(jira-dc): don't org-gate a personal-workspace Jira DC integration (#15036)

This commit is contained in:
Alona
2026-06-30 11:56:55 -05:00
committed by GitHub
parent 94ddd67631
commit 6bbb4f3e19
2 changed files with 118 additions and 1 deletions
@@ -90,6 +90,12 @@ async def _workspace_matches_context(
workspace_org_id = getattr(workspace, 'org_id', None)
if workspace_org_id is None:
return True
# A workspace stamped with its creator's *personal* org (org_id == the
# admin's own user id) is an instance-wide integration, not a tenant
# boundary, so don't org-gate it. Real team/default orgs keep enforcing.
admin_user_id = getattr(workspace, 'admin_user_id', None)
if admin_user_id is not None and str(workspace_org_id) == str(admin_user_id):
return True
return await _effective_org_matches(
workspace_id=workspace.id,
workspace_org_id=workspace_org_id,
@@ -32,7 +32,7 @@ class FakeUserContext:
return self.user_id
def _linked_store(*, org_id: UUID | None = ORG_ID):
def _linked_store(*, org_id: UUID | None = ORG_ID, admin_user_id: str = 'admin-user'):
store = MagicMock()
store.get_user_by_active_workspace = AsyncMock(
return_value=SimpleNamespace(jira_dc_workspace_id=7)
@@ -43,6 +43,7 @@ def _linked_store(*, org_id: UUID | None = ORG_ID):
name='jira.example.com',
status='active',
org_id=org_id,
admin_user_id=admin_user_id,
)
)
return store
@@ -197,3 +198,113 @@ async def test_enricher_propagates_token_error_for_jira_triggered_start():
jwt_service=MagicMock(),
access_token_hard_timeout=timedelta(minutes=5),
)
# A personal org's id == its owner's user id, so a JDC workspace created in
# personal-workspace mode is stamped org_id == admin_user_id.
PERSONAL_ORG_ID = UUID('00000000-0000-0000-0000-000000000789')
@pytest.mark.asyncio
async def test_enricher_allows_personal_workspace_org_for_non_creator():
# Personal-workspace install: the JDC workspace (one company's Jira) is
# stamped with its creator's personal org. A *different* user must still get
# their own token -- it's an instance-wide integration, not a tenant boundary.
store = _linked_store(org_id=PERSONAL_ORG_ID, admin_user_id=str(PERSONAL_ORG_ID))
jwt_service = MagicMock()
jwt_service.create_jws_token.return_value = 'signed-token'
with (
patch(
'integrations.jira_dc.jira_dc_conversation_secret_enricher.JIRA_DC_ENABLE_OAUTH',
True,
),
patch(
'integrations.jira_dc.jira_dc_conversation_secret_enricher.JIRA_DC_BASE_URL',
'https://jira.example.com',
),
patch(
'integrations.jira_dc.jira_dc_conversation_secret_enricher.JiraDcIntegrationStore.get_instance',
return_value=store,
),
):
enrichment = await JiraDcConversationSecretEnricher().enrich(
user_context=FakeUserContext(user_id='other-user', org_id=ORG_ID),
user=MagicMock(id='other-user'),
trigger=ConversationTrigger.SLACK,
system_message_suffix=None,
web_url='https://openhands.example.com',
jwt_service=jwt_service,
access_token_hard_timeout=timedelta(minutes=5),
)
assert 'JIRA_DC_TOKEN' in enrichment.secrets
assert 'JIRA_DC_TOKEN' in (enrichment.system_message_suffix or '')
@pytest.mark.asyncio
async def test_enricher_still_blocks_real_cross_org_workspace():
# A genuine team/default org (org_id != admin_user_id) keeps enforcing
# isolation: a user from a different org gets no token.
store = _linked_store(org_id=OTHER_ORG_ID, admin_user_id='admin-user')
with (
patch(
'integrations.jira_dc.jira_dc_conversation_secret_enricher.JIRA_DC_ENABLE_OAUTH',
True,
),
patch(
'integrations.jira_dc.jira_dc_conversation_secret_enricher.JIRA_DC_BASE_URL',
'https://jira.example.com',
),
patch(
'integrations.jira_dc.jira_dc_conversation_secret_enricher.JiraDcIntegrationStore.get_instance',
return_value=store,
),
):
enrichment = await JiraDcConversationSecretEnricher().enrich(
user_context=FakeUserContext(org_id=ORG_ID),
user=MagicMock(id='kc-user'),
trigger=ConversationTrigger.SLACK,
system_message_suffix='Existing instructions.',
web_url='https://openhands.example.com',
jwt_service=MagicMock(),
access_token_hard_timeout=timedelta(minutes=5),
)
assert enrichment.secrets == {}
assert enrichment.system_message_suffix == 'Existing instructions.'
@pytest.mark.asyncio
async def test_enricher_injects_for_null_org_workspace():
# An unscoped workspace (org_id is None) is instance-wide -- regression guard.
store = _linked_store(org_id=None)
jwt_service = MagicMock()
jwt_service.create_jws_token.return_value = 'signed-token'
with (
patch(
'integrations.jira_dc.jira_dc_conversation_secret_enricher.JIRA_DC_ENABLE_OAUTH',
True,
),
patch(
'integrations.jira_dc.jira_dc_conversation_secret_enricher.JIRA_DC_BASE_URL',
'https://jira.example.com',
),
patch(
'integrations.jira_dc.jira_dc_conversation_secret_enricher.JiraDcIntegrationStore.get_instance',
return_value=store,
),
):
enrichment = await JiraDcConversationSecretEnricher().enrich(
user_context=FakeUserContext(user_id='anyone', org_id=None),
user=MagicMock(id='anyone'),
trigger=ConversationTrigger.SLACK,
system_message_suffix=None,
web_url='https://openhands.example.com',
jwt_service=jwt_service,
access_token_hard_timeout=timedelta(minutes=5),
)
assert 'JIRA_DC_TOKEN' in enrichment.secrets