mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-09 04:40:57 +00:00
fix(cloud): treat workspace owners as Space-bound (#2378)
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
This commit is contained in:
@@ -285,8 +285,17 @@ class UserRouterGroup(group.RouterGroup):
|
|||||||
request_context.workspace_uuid,
|
request_context.workspace_uuid,
|
||||||
)
|
)
|
||||||
owner = await self.ap.user_service.get_workspace_owner(access.workspace.uuid)
|
owner = await self.ap.user_service.get_workspace_owner(access.workspace.uuid)
|
||||||
owner_space_bound = bool(owner and owner.space_account_uuid)
|
cloud_mode = getattr(getattr(self.ap, 'deployment', None), 'mode', 'oss') == 'cloud'
|
||||||
credits = await self.ap.space_service.get_credits(owner.user) if owner_space_bound else None
|
owner_has_local_space_credentials = bool(owner and owner.space_account_uuid)
|
||||||
|
# Cloud Accounts authenticate through LangBot Account, so every projected
|
||||||
|
# Workspace owner is already bound even when this Core has no local OAuth
|
||||||
|
# token row (model billing uses the owner's control-plane API key).
|
||||||
|
owner_space_bound = cloud_mode or owner_has_local_space_credentials
|
||||||
|
credits = (
|
||||||
|
await self.ap.space_service.get_credits(owner.user)
|
||||||
|
if owner is not None and owner.space_account_uuid
|
||||||
|
else None
|
||||||
|
)
|
||||||
return self.success(
|
return self.success(
|
||||||
data={
|
data={
|
||||||
'credits': credits,
|
'credits': credits,
|
||||||
|
|||||||
@@ -272,9 +272,10 @@ async def test_space_credits_are_resolved_from_workspace_owner(space_oauth_api):
|
|||||||
'/api/v1/user/space-credits',
|
'/api/v1/user/space-credits',
|
||||||
headers={'Authorization': 'Bearer account-token', 'X-Workspace-Id': WORKSPACE_UUID},
|
headers={'Authorization': 'Bearer account-token', 'X-Workspace-Id': WORKSPACE_UUID},
|
||||||
)
|
)
|
||||||
|
payload = await response.get_json()
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert (await response.get_json())['data'] == {
|
assert payload['data'] == {
|
||||||
'credits': 25000,
|
'credits': 25000,
|
||||||
'owner_space_bound': True,
|
'owner_space_bound': True,
|
||||||
'is_workspace_owner': True,
|
'is_workspace_owner': True,
|
||||||
@@ -282,6 +283,28 @@ async def test_space_credits_are_resolved_from_workspace_owner(space_oauth_api):
|
|||||||
application.space_service.get_credits.assert_awaited_once_with('owner@example.com')
|
application.space_service.get_credits.assert_awaited_once_with('owner@example.com')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_cloud_workspace_owner_is_always_space_bound_after_login(space_oauth_api):
|
||||||
|
application, client = space_oauth_api
|
||||||
|
application.deployment.mode = 'cloud'
|
||||||
|
application.user_service.get_workspace_owner = AsyncMock(return_value=None)
|
||||||
|
application.space_service.get_credits = AsyncMock()
|
||||||
|
|
||||||
|
response = await client.get(
|
||||||
|
'/api/v1/user/space-credits',
|
||||||
|
headers={'Authorization': 'Bearer account-token', 'X-Workspace-Id': WORKSPACE_UUID},
|
||||||
|
)
|
||||||
|
payload = await response.get_json()
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert payload['data'] == {
|
||||||
|
'credits': None,
|
||||||
|
'owner_space_bound': True,
|
||||||
|
'is_workspace_owner': True,
|
||||||
|
}
|
||||||
|
application.space_service.get_credits.assert_not_awaited()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_bind_callback_uses_opaque_state_and_never_treats_it_as_jwt(space_oauth_api):
|
async def test_bind_callback_uses_opaque_state_and_never_treats_it_as_jwt(space_oauth_api):
|
||||||
application, client = space_oauth_api
|
application, client = space_oauth_api
|
||||||
|
|||||||
Reference in New Issue
Block a user