Skip to content

Commit adceff1

Browse files
nits w/ authentication
1 parent 5ab973b commit adceff1

File tree

5 files changed

+40
-36
lines changed

5 files changed

+40
-36
lines changed

packages/backend/src/bitbucket.ts

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { createBitbucketCloudClient } from "@coderabbitai/bitbucket/cloud";
2-
import { createBitbucketServerClient } from "@coderabbitai/bitbucket/server";
1+
import { createBitbucketCloudClient as createBitbucketCloudClientBase } from "@coderabbitai/bitbucket/cloud";
2+
import { createBitbucketServerClient as createBitbucketServerClientBase } from "@coderabbitai/bitbucket/server";
33
import { BitbucketConnectionConfig } from "@sourcebot/schemas/v3/bitbucket.type";
44
import type { ClientOptions, ClientPathsWithMethod } from "openapi-fetch";
55
import { createLogger } from "@sourcebot/shared";
@@ -36,10 +36,10 @@ interface BitbucketClient {
3636
shouldExcludeRepo: (repo: BitbucketRepository, config: BitbucketConnectionConfig) => boolean;
3737
}
3838

39-
type CloudAPI = ReturnType<typeof createBitbucketCloudClient>;
39+
type CloudAPI = ReturnType<typeof createBitbucketCloudClientBase>;
4040
type CloudGetRequestPath = ClientPathsWithMethod<CloudAPI, "get">;
4141

42-
type ServerAPI = ReturnType<typeof createBitbucketServerClient>;
42+
type ServerAPI = ReturnType<typeof createBitbucketServerClientBase>;
4343
type ServerGetRequestPath = ClientPathsWithMethod<ServerAPI, "get">;
4444

4545
type CloudPaginatedResponse<T> = {
@@ -70,8 +70,8 @@ export const getBitbucketReposFromConfig = async (config: BitbucketConnectionCon
7070
}
7171

7272
const client = config.deploymentType === 'server' ?
73-
serverClient(config.url!, config.user, token) :
74-
cloudClient(config.user, token);
73+
createBitbucketServerClient(config.url!, config.user, token) :
74+
createBitbucketCloudClient(config.user, token);
7575

7676
let allRepos: BitbucketRepository[] = [];
7777
let allWarnings: string[] = [];
@@ -104,11 +104,10 @@ export const getBitbucketReposFromConfig = async (config: BitbucketConnectionCon
104104
};
105105
}
106106

107-
function cloudClient(user: string | undefined, token: string | undefined): BitbucketClient {
108-
107+
export function createBitbucketCloudClient(user: string | undefined, token: string | undefined): BitbucketClient {
109108
const authorizationString =
110109
token
111-
? !user || user == "x-token-auth"
110+
? (!user || user === "x-token-auth")
112111
? `Bearer ${token}`
113112
: `Basic ${Buffer.from(`${user}:${token}`).toString('base64')}`
114113
: undefined;
@@ -121,7 +120,7 @@ function cloudClient(user: string | undefined, token: string | undefined): Bitbu
121120
},
122121
};
123122

124-
const apiClient = createBitbucketCloudClient(clientOptions);
123+
const apiClient = createBitbucketCloudClientBase(clientOptions);
125124
var client: BitbucketClient = {
126125
deploymentType: BITBUCKET_CLOUD,
127126
token: token,
@@ -380,7 +379,7 @@ export function cloudShouldExcludeRepo(repo: BitbucketRepository, config: Bitbuc
380379
return false;
381380
}
382381

383-
function serverClient(url: string, user: string | undefined, token: string | undefined): BitbucketClient {
382+
function createBitbucketServerClient(url: string, user: string | undefined, token: string | undefined): BitbucketClient {
384383
const authorizationString = (() => {
385384
// If we're not given any credentials we return an empty auth string. This will only work if the project/repos are public
386385
if(!user && !token) {
@@ -402,7 +401,7 @@ function serverClient(url: string, user: string | undefined, token: string | und
402401
},
403402
};
404403

405-
const apiClient = createBitbucketServerClient(clientOptions);
404+
const apiClient = createBitbucketServerClientBase(clientOptions);
406405
var client: BitbucketClient = {
407406
deploymentType: BITBUCKET_SERVER,
408407
token: token,
@@ -604,22 +603,14 @@ export function serverShouldExcludeRepo(repo: BitbucketRepository, config: Bitbu
604603
* @see https://developer.atlassian.com/cloud/bitbucket/rest/api-group-repositories/#api-repositories-workspace-repo-slug-permissions-config-users-get
605604
*/
606605
export const getExplicitUserPermissionsForCloudRepo = async (
606+
client: BitbucketClient,
607607
workspace: string,
608608
repoSlug: string,
609-
token: string | undefined,
610609
): Promise<Array<{ accountId: string }>> => {
611-
const apiClient = createBitbucketCloudClient({
612-
baseUrl: BITBUCKET_CLOUD_API,
613-
headers: {
614-
Accept: "application/json",
615-
...(token ? { Authorization: `Bearer ${token}` } : {}),
616-
},
617-
});
618-
619610
const path = `/repositories/${workspace}/${repoSlug}/permissions-config/users` as CloudGetRequestPath;
620611

621612
const users = await getPaginatedCloud<CloudRepositoryUserPermission>(path, async (p, query) => {
622-
const response = await apiClient.GET(p, {
613+
const response = await client.apiClient.GET(p, {
623614
params: {
624615
path: { workspace, repo_slug: repoSlug },
625616
query,
@@ -644,20 +635,12 @@ export const getExplicitUserPermissionsForCloudRepo = async (
644635
* @see https://developer.atlassian.com/cloud/bitbucket/rest/api-group-repositories/#api-user-permissions-repositories-get
645636
*/
646637
export const getReposForAuthenticatedBitbucketCloudUser = async (
647-
accessToken: string,
638+
client: BitbucketClient,
648639
): Promise<Array<{ uuid: string }>> => {
649-
const apiClient = createBitbucketCloudClient({
650-
baseUrl: BITBUCKET_CLOUD_API,
651-
headers: {
652-
Accept: "application/json",
653-
Authorization: `Bearer ${accessToken}`,
654-
},
655-
});
656-
657640
const path = `/user/permissions/repositories` as CloudGetRequestPath;
658641

659642
const permissions = await getPaginatedCloud<CloudRepositoryPermission>(path, async (p, query) => {
660-
const response = await apiClient.GET(p, {
643+
const response = await client.apiClient.GET(p, {
661644
params: { query },
662645
});
663646
const { data, error } = response;

packages/backend/src/ee/accountPermissionSyncer.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
getOAuthScopesForAuthenticatedUser as getGitLabOAuthScopesForAuthenticatedUser,
1515
getProjectsForAuthenticatedUser,
1616
} from "../gitlab.js";
17-
import { getReposForAuthenticatedBitbucketCloudUser } from "../bitbucket.js";
17+
import { createBitbucketCloudClient, getReposForAuthenticatedBitbucketCloudUser } from "../bitbucket.js";
1818
import { Settings } from "../types.js";
1919
import { setIntervalAsync } from "../utils.js";
2020

@@ -273,7 +273,10 @@ export class AccountPermissionSyncer {
273273
throw new Error(`User '${account.user.email}' does not have a Bitbucket Cloud OAuth access token associated with their account. Please re-authenticate with Bitbucket Cloud to refresh the token.`);
274274
}
275275

276-
const bitbucketRepos = await getReposForAuthenticatedBitbucketCloudUser(accessToken);
276+
// @note: we don't pass a user here since we want to use a bearer token
277+
// for authentication.
278+
const client = createBitbucketCloudClient(/* user = */ undefined, accessToken)
279+
const bitbucketRepos = await getReposForAuthenticatedBitbucketCloudUser(client);
277280
const bitbucketRepoUuids = bitbucketRepos.map(repo => repo.uuid);
278281

279282
const repos = await this.db.repo.findMany({

packages/backend/src/ee/repoPermissionSyncer.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import { Redis } from 'ioredis';
77
import { PERMISSION_SYNC_SUPPORTED_CODE_HOST_TYPES } from "../constants.js";
88
import { createOctokitFromToken, getRepoCollaborators, GITHUB_CLOUD_HOSTNAME } from "../github.js";
99
import { createGitLabFromPersonalAccessToken, getProjectMembers } from "../gitlab.js";
10-
import { getExplicitUserPermissionsForCloudRepo } from "../bitbucket.js";
10+
import { createBitbucketCloudClient, getExplicitUserPermissionsForCloudRepo } from "../bitbucket.js";
1111
import { repoMetadataSchema } from "@sourcebot/shared";
1212
import { Settings } from "../types.js";
1313
import { getAuthCredentialsForRepo, setIntervalAsync } from "../utils.js";
14+
import { BitbucketConnectionConfig } from "@sourcebot/schemas/v3/index.type";
1415

1516
type RepoPermissionSyncJob = {
1617
jobId: string;
@@ -238,6 +239,13 @@ export class RepoPermissionSyncer {
238239

239240
return accounts.map(account => account.id);
240241
} else if (repo.external_codeHostType === 'bitbucketCloud') {
242+
const config = credentials.connectionConfig as BitbucketConnectionConfig | undefined;
243+
if (!config) {
244+
throw new Error(`No connection config found for repo ${id}`);
245+
}
246+
247+
const client = createBitbucketCloudClient(config.user, credentials.token);
248+
241249
const parsedMetadata = repoMetadataSchema.safeParse(repo.metadata);
242250
const bitbucketCloudMetadata = parsedMetadata.success ? parsedMetadata.data.codeHostMetadata?.bitbucketCloud : undefined;
243251
if (!bitbucketCloudMetadata) {
@@ -253,7 +261,7 @@ export class RepoPermissionSyncer {
253261
// but there may be a delay of up to `experiment_userDrivenPermissionSyncIntervalMs` before
254262
// they see the repository in Sourcebot.
255263
// @see: https://developer.atlassian.com/cloud/bitbucket/rest/api-group-repositories/#api-repositories-workspace-repo-slug-permissions-config-users-get
256-
const users = await getExplicitUserPermissionsForCloudRepo(workspace, repoSlug, credentials.token);
264+
const users = await getExplicitUserPermissionsForCloudRepo(client, workspace, repoSlug);
257265
const userAccountIds = users.map(u => u.accountId);
258266

259267
const accounts = await this.db.account.findMany({

packages/backend/src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Connection, Repo, RepoToConnection } from "@sourcebot/db";
2+
import { ConnectionConfig } from "@sourcebot/schemas/v3/connection.type";
23
import { Settings as SettingsSchema } from "@sourcebot/schemas/v3/index.type";
34

45
export type Settings = Required<SettingsSchema>;
@@ -19,4 +20,8 @@ export type RepoAuthCredentials = {
1920
token: string;
2021
cloneUrlWithToken?: string;
2122
authHeader?: string;
23+
/** The connection that configured the
24+
* credentials for this repo.
25+
*/
26+
connectionConfig?: ConnectionConfig;
2227
}

packages/backend/src/utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export const getAuthCredentialsForRepo = async (repo: RepoWithConnections, logge
145145
password: token,
146146
}
147147
),
148+
connectionConfig: config,
148149
}
149150
}
150151
} else if (connection.connectionType === 'gitlab') {
@@ -161,6 +162,7 @@ export const getAuthCredentialsForRepo = async (repo: RepoWithConnections, logge
161162
password: token
162163
}
163164
),
165+
connectionConfig: config,
164166
}
165167
}
166168
} else if (connection.connectionType === 'gitea') {
@@ -176,6 +178,7 @@ export const getAuthCredentialsForRepo = async (repo: RepoWithConnections, logge
176178
password: token
177179
}
178180
),
181+
connectionConfig: config,
179182
}
180183
}
181184
} else if (connection.connectionType === 'bitbucket') {
@@ -193,6 +196,7 @@ export const getAuthCredentialsForRepo = async (repo: RepoWithConnections, logge
193196
password: token
194197
}
195198
),
199+
connectionConfig: config,
196200
}
197201
}
198202
} else if (connection.connectionType === 'azuredevops') {
@@ -223,6 +227,7 @@ export const getAuthCredentialsForRepo = async (repo: RepoWithConnections, logge
223227
password: token
224228
}
225229
),
230+
connectionConfig: config,
226231
}
227232
}
228233
}

0 commit comments

Comments
 (0)