mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 16:37:52 +08:00
Merge pull request #4572 from catoncat/fix/openai-agent-identity-team-isolation
fix(openai): 按 Team 隔离 Agent Identity 导入
This commit is contained in:
@@ -47,6 +47,100 @@ func TestNormalizeCodexImportEntryAcceptsAgentIdentityAuthJSON(t *testing.T) {
|
||||
require.NotEmpty(t, item.WarningTexts)
|
||||
}
|
||||
|
||||
func TestBuildCodexAgentIdentityKeysUseChatGPTAccountOnly(t *testing.T) {
|
||||
keys := buildCodexAgentIdentityKeys("team-a")
|
||||
require.Equal(t, []string{"account:team-a"}, keys)
|
||||
}
|
||||
|
||||
func TestCodexAgentIdentityIndexSeparatesTeamsForSameUser(t *testing.T) {
|
||||
existing := service.Account{
|
||||
ID: 1,
|
||||
Credentials: map[string]any{
|
||||
"auth_mode": service.OpenAIAuthModeAgentIdentity,
|
||||
"chatgpt_account_id": "team-a",
|
||||
"chatgpt_user_id": "same-user",
|
||||
"agent_runtime_id": "runtime-a",
|
||||
},
|
||||
}
|
||||
index := buildCodexAccountIndex([]service.Account{existing})
|
||||
|
||||
teamBKeys := buildCodexAgentIdentityKeys("team-b")
|
||||
matched, _ := index.Find(teamBKeys, "same-user")
|
||||
require.Nil(t, matched)
|
||||
|
||||
teamAKeys := buildCodexAgentIdentityKeys("team-a")
|
||||
matched, matchedKey := index.Find(teamAKeys, "same-user")
|
||||
require.NotNil(t, matched)
|
||||
require.Equal(t, int64(1), matched.ID)
|
||||
require.Equal(t, "account:team-a", matchedKey)
|
||||
}
|
||||
|
||||
func TestImportCodexSessionsKeepsAgentIdentityTeamsSeparate(t *testing.T) {
|
||||
first := buildAgentIdentityImportValue(t, "runtime-a", "team-a", "same-user", "task-a")
|
||||
second := buildAgentIdentityImportValue(t, "runtime-b", "team-b", "same-user", "task-b")
|
||||
svc := newCodexImportMemoryAdminService(nil)
|
||||
handler := NewAccountHandler(svc, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
||||
|
||||
result, err := handler.importCodexSessions(context.Background(), CodexSessionImportRequest{
|
||||
SkipDefaultGroupBind: boolPtr(true),
|
||||
}, []codexImportEntry{{Index: 1, Value: first}, {Index: 2, Value: second}})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, result.Created)
|
||||
require.Zero(t, result.Updated)
|
||||
require.Zero(t, result.Skipped)
|
||||
require.Len(t, svc.createdAccounts, 2)
|
||||
}
|
||||
|
||||
func TestImportCodexSessionsMergesAgentIdentityRuntimesForSameTeam(t *testing.T) {
|
||||
first := buildAgentIdentityImportValue(t, "runtime-a", "team-a", "same-user", "task-a")
|
||||
second := buildAgentIdentityImportValue(t, "runtime-b", "team-a", "same-user", "task-b")
|
||||
firstIdentity, ok := first["agent_identity"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
existing := service.Account{
|
||||
ID: 41,
|
||||
Platform: service.PlatformOpenAI,
|
||||
Type: service.AccountTypeOAuth,
|
||||
Credentials: map[string]any{
|
||||
"auth_mode": service.OpenAIAuthModeAgentIdentity,
|
||||
"agent_runtime_id": firstIdentity["agent_runtime_id"],
|
||||
"agent_private_key": firstIdentity["agent_private_key"],
|
||||
"task_id": firstIdentity["task_id"],
|
||||
"chatgpt_account_id": firstIdentity["account_id"],
|
||||
"chatgpt_user_id": firstIdentity["chatgpt_user_id"],
|
||||
},
|
||||
}
|
||||
svc := newCodexImportMemoryAdminService([]service.Account{existing})
|
||||
handler := NewAccountHandler(svc, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
||||
|
||||
result, err := handler.importCodexSessions(context.Background(), CodexSessionImportRequest{
|
||||
SkipDefaultGroupBind: boolPtr(true),
|
||||
}, []codexImportEntry{{Index: 1, Value: second}})
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, result.Created)
|
||||
require.Equal(t, 1, result.Updated)
|
||||
require.Len(t, svc.updatedAccounts, 1)
|
||||
require.Equal(t, "runtime-b", svc.updatedAccounts[0].input.Credentials["agent_runtime_id"])
|
||||
require.Equal(t, "task-b", svc.updatedAccounts[0].input.Credentials["task_id"])
|
||||
}
|
||||
|
||||
func buildAgentIdentityImportValue(t *testing.T, runtimeID, accountID, userID, taskID string) map[string]any {
|
||||
t.Helper()
|
||||
_, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
||||
require.NoError(t, err)
|
||||
der, err := x509.MarshalPKCS8PrivateKey(privateKey)
|
||||
require.NoError(t, err)
|
||||
return map[string]any{
|
||||
"auth_mode": "agentIdentity",
|
||||
"agent_identity": map[string]any{
|
||||
"agent_runtime_id": runtimeID,
|
||||
"agent_private_key": base64.StdEncoding.EncodeToString(der),
|
||||
"task_id": taskID,
|
||||
"account_id": accountID,
|
||||
"chatgpt_user_id": userID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportCodexSessionsCreatesAgentIdentityWithoutOAuthExpiry(t *testing.T) {
|
||||
_, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -532,7 +532,7 @@ func normalizeCodexImportEntry(entry codexImportEntry) (*codexImportAccount, err
|
||||
if item.AgentTaskID == "" {
|
||||
item.WarningTexts = append(item.WarningTexts, "未包含 task_id,首次请求会使用现有 runtime 注册新 task")
|
||||
}
|
||||
item.IdentityKeys = buildCodexAgentIdentityKeys(item.AccountID, item.UserID, item.Email, item.AgentRuntimeID)
|
||||
item.IdentityKeys = buildCodexAgentIdentityKeys(item.AccountID)
|
||||
item.Name = buildCodexImportAccountName(item, entry.Index)
|
||||
return item, nil
|
||||
}
|
||||
@@ -894,12 +894,17 @@ func buildCodexImportIdentityKeys(accountID, userID, email, accessToken, refresh
|
||||
return buildCodexStoredIdentityKeys(accountID, userID, email, accessToken)
|
||||
}
|
||||
|
||||
func buildCodexAgentIdentityKeys(accountID, userID, email, runtimeID string) []string {
|
||||
keys := buildCodexStoredIdentityKeys(accountID, userID, email, "")
|
||||
if runtimeID = strings.TrimSpace(runtimeID); runtimeID != "" {
|
||||
keys = append([]string{"agent:" + runtimeID}, keys...)
|
||||
func buildCodexAgentIdentityKeys(accountID string) []string {
|
||||
// Agent Identity credentials belonging to the same ChatGPT account are
|
||||
// intentionally merged, while the same user may own multiple accounts.
|
||||
// Do not use user/email/runtime as fallback keys here: user_id is shared
|
||||
// across Team workspaces and runtime_id changes when a new runtime is
|
||||
// registered for the same account.
|
||||
accountID = strings.TrimSpace(accountID)
|
||||
if accountID == "" {
|
||||
return nil
|
||||
}
|
||||
return keys
|
||||
return []string{"account:" + accountID}
|
||||
}
|
||||
|
||||
// buildCodexStoredIdentityKeys 生成存量账号索引键,保留 user/account 维度,
|
||||
|
||||
Reference in New Issue
Block a user