Skip to content

Commit 12e0a73

Browse files
committed
WIP [server] add ScmService.getToken
1 parent 65698ca commit 12e0a73

File tree

6 files changed

+42
-22
lines changed

6 files changed

+42
-22
lines changed

components/server/src/container-module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ import { IncrementalWorkspaceService } from "./prebuilds/incremental-workspace-s
8686
import { PrebuildManager } from "./prebuilds/prebuild-manager";
8787
import { PrebuildStatusMaintainer } from "./prebuilds/prebuilt-status-maintainer";
8888
import { ProjectsService } from "./projects/projects-service";
89-
import { ScmService } from "./projects/scm-service";
9089
import { RedisMutex } from "./redis/mutex";
9190
import { Server } from "./server";
9291
import { SessionHandler } from "./session-handler";
@@ -128,6 +127,7 @@ import { WorkspaceStartController } from "./workspace/workspace-start-controller
128127
import { WorkspaceStarter } from "./workspace/workspace-starter";
129128
import { DefaultWorkspaceImageValidator } from "./orgs/default-workspace-image-validator";
130129
import { ContextAwareAnalyticsWriter } from "./analytics";
130+
import { ScmService } from "./scm/scm-service";
131131

132132
export const productionContainerModule = new ContainerModule(
133133
(bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => {

components/server/src/projects/projects-service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ import { ErrorCodes, ApplicationError } from "@gitpod/gitpod-protocol/lib/messag
2828
import { URL } from "url";
2929
import { Authorizer, SYSTEM_USER } from "../authorization/authorizer";
3030
import { TransactionalContext } from "@gitpod/gitpod-db/lib/typeorm/transactional-db-impl";
31-
import { ScmService } from "./scm-service";
3231
import { daysBefore, isDateSmaller } from "@gitpod/gitpod-protocol/lib/util/timeutil";
32+
import { ScmService } from "../scm/scm-service";
3333

3434
const MAX_PROJECT_NAME_LENGTH = 100;
3535

components/server/src/projects/scm-service.ts renamed to components/server/src/scm/scm-service.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,31 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { Project, User } from "@gitpod/gitpod-protocol";
8-
import { RepoURL } from "../repohost";
97
import { inject, injectable } from "inversify";
8+
import { Authorizer } from "../authorization/authorizer";
9+
import { Config } from "../config";
10+
import { TokenProvider } from "../user/token-provider";
11+
import { Project, Token, User } from "@gitpod/gitpod-protocol";
1012
import { HostContextProvider } from "../auth/host-context-provider";
13+
import { RepoURL } from "../repohost";
1114
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
1215

1316
@injectable()
1417
export class ScmService {
15-
constructor(@inject(HostContextProvider) private readonly hostContextProvider: HostContextProvider) {}
18+
constructor(
19+
@inject(Config) protected readonly config: Config,
20+
@inject(Authorizer) private readonly auth: Authorizer,
21+
@inject(TokenProvider) private readonly tokenProvider: TokenProvider,
22+
@inject(HostContextProvider) private readonly hostContextProvider: HostContextProvider,
23+
) {}
24+
25+
public async getToken(userId: string, query: { host: string }): Promise<Token> {
26+
// FIXME(at) this doesn't sound right. "token" is pretty overloaded, thus `read_scm_tokens` would be correct
27+
await this.auth.checkPermissionOnUser(userId, "read_tokens", userId);
28+
const { host } = query;
29+
const token = await this.tokenProvider.getTokenForHost(userId, host);
30+
return token;
31+
}
1632

1733
async installWebhookForPrebuilds(project: Project, installer: User) {
1834
// Install the prebuilds webhook if possible

components/server/src/user/token-provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ export interface TokenProvider {
1313
* @param user
1414
* @param host
1515
*/
16-
getTokenForHost(user: User, host: string): Promise<Token>;
16+
getTokenForHost(user: User | string, host: string): Promise<Token>;
1717
}

components/server/src/user/token-service.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,43 @@ import { HostContextProvider } from "../auth/host-context-provider";
1010
import { UserDB } from "@gitpod/gitpod-db/lib";
1111
import { v4 as uuidv4 } from "uuid";
1212
import { TokenProvider } from "./token-provider";
13+
import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
1314

1415
@injectable()
1516
export class TokenService implements TokenProvider {
1617
static readonly GITPOD_AUTH_PROVIDER_ID = "Gitpod";
17-
static readonly GITPOD_PORT_AUTH_TOKEN_EXPIRY_MILLIS = 30 * 60 * 1000;
1818

1919
@inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider;
2020
@inject(UserDB) protected readonly userDB: UserDB;
2121

2222
protected getTokenForHostCache = new Map<string, Promise<Token>>();
2323

24-
async getTokenForHost(user: User, host: string): Promise<Token> {
24+
async getTokenForHost(user: User | string, host: string): Promise<Token> {
25+
const userId = User.is(user) ? user.id : user;
2526
// (AT) when it comes to token renewal, the awaited http requests may
2627
// cause "parallel" calls to repeat the renewal, which will fail.
2728
// Caching for pending operations should solve this issue.
28-
const key = `${host}-${user.id}`;
29+
const key = `${host}-${userId}`;
2930
let promise = this.getTokenForHostCache.get(key);
3031
if (!promise) {
31-
promise = this.doGetTokenForHost(user, host);
32+
promise = this.doGetTokenForHost(userId, host);
3233
this.getTokenForHostCache.set(key, promise);
3334
promise = promise.finally(() => this.getTokenForHostCache.delete(key));
3435
}
3536
return promise;
3637
}
3738

38-
async doGetTokenForHost(user: User, host: string): Promise<Token> {
39+
private async doGetTokenForHost(userId: string, host: string): Promise<Token> {
40+
const user = await this.userDB.findUserById(userId);
41+
if (!user) {
42+
throw new ApplicationError(ErrorCodes.NOT_FOUND, `User (${userId}) not found.`);
43+
}
3944
const identity = this.getIdentityForHost(user, host);
4045
let token = await this.userDB.findTokenForIdentity(identity);
4146
if (!token) {
42-
throw new Error(
43-
`No token found for user ${identity.authProviderId}/${identity.authId}/${identity.authName}!`,
47+
throw new ApplicationError(
48+
ErrorCodes.NOT_FOUND,
49+
`SCM Token not found: (${userId}/${identity?.authId}/${identity?.authName}).`,
4450
);
4551
}
4652
const aboutToExpireTime = new Date();
@@ -84,16 +90,16 @@ export class TokenService implements TokenProvider {
8490
return await this.userDB.addToken(identity, token);
8591
}
8692

87-
protected getIdentityForHost(user: User, host: string): Identity {
93+
private getIdentityForHost(user: User, host: string): Identity {
8894
const authProviderId = this.getAuthProviderId(host);
8995
const hostIdentity = authProviderId && User.getIdentity(user, authProviderId);
9096
if (!hostIdentity) {
91-
throw new Error(`User ${user.name} has no identity for host: ${host}!`);
97+
throw new ApplicationError(ErrorCodes.NOT_FOUND, `User (${user.id}) has no identity for host: ${host}.`);
9298
}
9399
return hostIdentity;
94100
}
95101

96-
protected getAuthProviderId(host: string): string | undefined {
102+
private getAuthProviderId(host: string): string | undefined {
97103
const hostContext = this.hostContextProvider.get(host);
98104
if (!hostContext) {
99105
return undefined;

components/server/src/workspace/gitpod-server-impl.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ import { Config } from "../config";
108108
import { NotFoundError, UnauthorizedError } from "../errors";
109109
import { RepoURL } from "../repohost/repo-url";
110110
import { AuthorizationService } from "../user/authorization-service";
111-
import { TokenProvider } from "../user/token-provider";
112111
import { UserAuthentication } from "../user/user-authentication";
113112
import { ContextParser } from "./context-parser-service";
114113
import { GitTokenScopeGuesser } from "./git-token-scope-guesser";
@@ -172,6 +171,7 @@ import {
172171
} from "./suggested-repos-sorter";
173172
import { SubjectId } from "../auth/subject-id";
174173
import { runWithSubjectId } from "../util/request-context";
174+
import { ScmService } from "../scm/scm-service";
175175

176176
// shortcut
177177
export const traceWI = (ctx: TraceContext, wi: Omit<LogContext, "userId">) => TraceContext.setOWI(ctx, wi); // userId is already taken care of in WebsocketConnectionManager
@@ -203,7 +203,6 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
203203

204204
@inject(UserDB) private readonly userDB: UserDB,
205205
@inject(BlockedRepositoryDB) private readonly blockedRepostoryDB: BlockedRepositoryDB,
206-
@inject(TokenProvider) private readonly tokenProvider: TokenProvider,
207206
@inject(UserAuthentication) private readonly userAuthentication: UserAuthentication,
208207
@inject(UserService) private readonly userService: UserService,
209208
@inject(IAnalyticsWriter) private readonly analytics: IAnalyticsWriter,
@@ -229,6 +228,8 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
229228

230229
@inject(Authorizer) private readonly auth: Authorizer,
231230

231+
@inject(ScmService) private readonly scmService: ScmService,
232+
232233
@inject(BillingModes) private readonly billingModes: BillingModes,
233234
@inject(StripeService) private readonly stripeService: StripeService,
234235
@inject(UsageService) private readonly usageService: UsageService,
@@ -642,16 +643,13 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
642643
traceAPIParams(ctx, { query });
643644

644645
const user = await this.checkUser("getToken");
645-
const logCtx = { userId: user.id, host: query.host };
646-
647646
const { host } = query;
648647
try {
649-
const token = await this.tokenProvider.getTokenForHost(user, host);
648+
const token = await this.scmService.getToken(user.id, { host });
650649
await this.guardAccess({ kind: "token", subject: token, tokenOwnerID: user.id }, "get");
651650

652651
return token;
653652
} catch (error) {
654-
log.error(logCtx, "failed to find token: ", error);
655653
return undefined;
656654
}
657655
}

0 commit comments

Comments
 (0)