mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-10 07:18:10 -05:00
APP-216 Support multiple git providers in conversation secrets (#11908)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -526,13 +526,10 @@ class LiveStatusAppConversationService(AppConversationServiceBase):
|
||||
if not request.llm_model and parent_info.llm_model:
|
||||
request.llm_model = parent_info.llm_model
|
||||
|
||||
async def _setup_secrets_for_git_provider(
|
||||
self, git_provider: ProviderType | None, user: UserInfo
|
||||
) -> dict:
|
||||
"""Set up secrets for git provider authentication.
|
||||
async def _setup_secrets_for_git_providers(self, user: UserInfo) -> dict:
|
||||
"""Set up secrets for all git provider authentication.
|
||||
|
||||
Args:
|
||||
git_provider: The git provider type (GitHub, GitLab, etc.)
|
||||
user: User information containing authentication details
|
||||
|
||||
Returns:
|
||||
@@ -540,35 +537,42 @@ class LiveStatusAppConversationService(AppConversationServiceBase):
|
||||
"""
|
||||
secrets = await self.user_context.get_secrets()
|
||||
|
||||
if not git_provider:
|
||||
# Get all provider tokens from user authentication
|
||||
provider_tokens = await self.user_context.get_provider_tokens()
|
||||
if not provider_tokens:
|
||||
return secrets
|
||||
|
||||
secret_name = f'{git_provider.name}_TOKEN'
|
||||
# Create secrets for each provider token
|
||||
for provider_type, provider_token in provider_tokens.items():
|
||||
if not provider_token.token:
|
||||
continue
|
||||
|
||||
if self.web_url:
|
||||
# Create an access token for web-based authentication
|
||||
access_token = self.jwt_service.create_jws_token(
|
||||
payload={
|
||||
'user_id': user.id,
|
||||
'provider_type': git_provider.value,
|
||||
},
|
||||
expires_in=self.access_token_hard_timeout,
|
||||
)
|
||||
headers = {'X-Access-Token': access_token}
|
||||
secret_name = f'{provider_type.name}_TOKEN'
|
||||
|
||||
# Include keycloak_auth cookie in headers if app_mode is SaaS
|
||||
if self.app_mode == 'saas' and self.keycloak_auth_cookie:
|
||||
headers['Cookie'] = f'keycloak_auth={self.keycloak_auth_cookie}'
|
||||
if self.web_url:
|
||||
# Create an access token for web-based authentication
|
||||
access_token = self.jwt_service.create_jws_token(
|
||||
payload={
|
||||
'user_id': user.id,
|
||||
'provider_type': provider_type.value,
|
||||
},
|
||||
expires_in=self.access_token_hard_timeout,
|
||||
)
|
||||
headers = {'X-Access-Token': access_token}
|
||||
|
||||
secrets[secret_name] = LookupSecret(
|
||||
url=self.web_url + '/api/v1/webhooks/secrets',
|
||||
headers=headers,
|
||||
)
|
||||
else:
|
||||
# Use static token for environments without web URL access
|
||||
static_token = await self.user_context.get_latest_token(git_provider)
|
||||
if static_token:
|
||||
secrets[secret_name] = StaticSecret(value=static_token)
|
||||
# Include keycloak_auth cookie in headers if app_mode is SaaS
|
||||
if self.app_mode == 'saas' and self.keycloak_auth_cookie:
|
||||
headers['Cookie'] = f'keycloak_auth={self.keycloak_auth_cookie}'
|
||||
|
||||
secrets[secret_name] = LookupSecret(
|
||||
url=self.web_url + '/api/v1/webhooks/secrets',
|
||||
headers=headers,
|
||||
)
|
||||
else:
|
||||
# Use static token for environments without web URL access
|
||||
static_token = await self.user_context.get_latest_token(provider_type)
|
||||
if static_token:
|
||||
secrets[secret_name] = StaticSecret(value=static_token)
|
||||
|
||||
return secrets
|
||||
|
||||
@@ -768,8 +772,8 @@ class LiveStatusAppConversationService(AppConversationServiceBase):
|
||||
user = await self.user_context.get_user_info()
|
||||
workspace = LocalWorkspace(working_dir=working_dir)
|
||||
|
||||
# Set up secrets for git provider
|
||||
secrets = await self._setup_secrets_for_git_provider(git_provider, user)
|
||||
# Set up secrets for all git providers
|
||||
secrets = await self._setup_secrets_for_git_providers(user)
|
||||
|
||||
# Configure LLM and MCP
|
||||
llm, mcp_config = await self._configure_llm_and_mcp(user, llm_model)
|
||||
|
||||
@@ -9,7 +9,11 @@ from openhands.app_server.services.injector import InjectorState
|
||||
from openhands.app_server.user.specifiy_user_context import USER_CONTEXT_ATTR
|
||||
from openhands.app_server.user.user_context import UserContext, UserContextInjector
|
||||
from openhands.app_server.user.user_models import UserInfo
|
||||
from openhands.integrations.provider import ProviderHandler, ProviderType
|
||||
from openhands.integrations.provider import (
|
||||
PROVIDER_TOKEN_TYPE,
|
||||
ProviderHandler,
|
||||
ProviderType,
|
||||
)
|
||||
from openhands.sdk.conversation.secret_source import SecretSource, StaticSecret
|
||||
from openhands.server.user_auth.user_auth import UserAuth, get_user_auth
|
||||
|
||||
@@ -44,6 +48,9 @@ class AuthUserContext(UserContext):
|
||||
self._user_info = user_info
|
||||
return user_info
|
||||
|
||||
async def get_provider_tokens(self) -> PROVIDER_TOKEN_TYPE | None:
|
||||
return await self.user_auth.get_provider_tokens()
|
||||
|
||||
async def get_provider_handler(self):
|
||||
provider_handler = self._provider_handler
|
||||
if not provider_handler:
|
||||
|
||||
@@ -5,7 +5,7 @@ from fastapi import Request
|
||||
from openhands.app_server.errors import OpenHandsError
|
||||
from openhands.app_server.user.user_context import UserContext
|
||||
from openhands.app_server.user.user_models import UserInfo
|
||||
from openhands.integrations.provider import ProviderType
|
||||
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE, ProviderType
|
||||
from openhands.sdk.conversation.secret_source import SecretSource
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ class SpecifyUserContext(UserContext):
|
||||
async def get_authenticated_git_url(self, repository: str) -> str:
|
||||
raise NotImplementedError()
|
||||
|
||||
async def get_provider_tokens(self) -> PROVIDER_TOKEN_TYPE | None:
|
||||
raise NotImplementedError()
|
||||
|
||||
async def get_latest_token(self, provider_type: ProviderType) -> str | None:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from openhands.app_server.services.injector import Injector
|
||||
from openhands.app_server.user.user_models import (
|
||||
UserInfo,
|
||||
)
|
||||
from openhands.integrations.provider import ProviderType
|
||||
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE, ProviderType
|
||||
from openhands.sdk.conversation.secret_source import SecretSource
|
||||
from openhands.sdk.utils.models import DiscriminatedUnionMixin
|
||||
|
||||
@@ -26,6 +26,10 @@ class UserContext(ABC):
|
||||
async def get_authenticated_git_url(self, repository: str) -> str:
|
||||
"""Get the provider tokens for the user"""
|
||||
|
||||
@abstractmethod
|
||||
async def get_provider_tokens(self) -> PROVIDER_TOKEN_TYPE | None:
|
||||
"""Get the latest tokens for all provider types"""
|
||||
|
||||
@abstractmethod
|
||||
async def get_latest_token(self, provider_type: ProviderType) -> str | None:
|
||||
"""Get the latest token for the provider type given"""
|
||||
|
||||
Reference in New Issue
Block a user