Skip to content

Commit

Permalink
[server] Add organization image auth context to workspace image valid…
Browse files Browse the repository at this point in the history
…ation (#20560)

* [server] Add organization image auth context to workspace image validation

Tool: gitpod/catfood.gitpod.cloud

* Introduce `listOrgEnvVarsWithValues`

Tool: gitpod/catfood.gitpod.cloud
  • Loading branch information
filiptronicek authored Jan 29, 2025
1 parent 12f386b commit 77f3fde
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 8 deletions.
4 changes: 2 additions & 2 deletions components/server/src/container-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,9 +412,9 @@ export const productionContainerModule = new ContainerModule(
bind<DefaultWorkspaceImageValidator>(DefaultWorkspaceImageValidator)
.toDynamicValue((ctx) =>
// lazy load to avoid circular dependency
async (userId: string, imageRef: string) => {
async (userId: string, imageRef: string, organizationId?: string) => {
const user = await ctx.container.get(UserService).findUserById(userId, userId);
await ctx.container.get(WorkspaceService).validateImageRef({}, user, imageRef);
await ctx.container.get(WorkspaceService).validateImageRef({}, user, imageRef, organizationId);
},
)
.inSingletonScope();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
* See License.AGPL.txt in the project root for license information.
*/

export type DefaultWorkspaceImageValidator = (userId: string, imageRef: string) => Promise<void>;
export type DefaultWorkspaceImageValidator = (
userId: string,
imageRef: string,
organizationId?: string,
) => Promise<void>;
export const DefaultWorkspaceImageValidator = Symbol("DefaultWorkspaceImageValidator");
2 changes: 1 addition & 1 deletion components/server/src/orgs/organization-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ export class OrganizationService {
if (typeof settings.defaultWorkspaceImage === "string") {
const defaultWorkspaceImage = settings.defaultWorkspaceImage.trim();
if (defaultWorkspaceImage) {
await this.validateDefaultWorkspaceImage(userId, defaultWorkspaceImage);
await this.validateDefaultWorkspaceImage(userId, defaultWorkspaceImage, orgId);
settings = { ...settings, defaultWorkspaceImage };
} else {
settings = { ...settings, defaultWorkspaceImage: null };
Expand Down
7 changes: 7 additions & 0 deletions components/server/src/user/env-var-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ export class EnvVarService {
return this.orgDB.getOrgEnvironmentVariables(orgId);
}

async listOrgEnvVarsWithValues(requestorId: string, orgId: string): Promise<OrgEnvVarWithValue[]> {
const envVars = (await ApplicationError.notFoundToUndefined(this.listOrgEnvVars(requestorId, orgId))) ?? [];
const envVarValues = await this.orgDB.getOrgEnvironmentVariableValues(envVars);

return envVarValues;
}

async getOrgEnvVarById(requestorId: string, id: string): Promise<OrgEnvVar> {
const result = await this.orgDB.getOrgEnvironmentVariableById(id);
if (!result) {
Expand Down
16 changes: 12 additions & 4 deletions components/server/src/workspace/workspace-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1407,9 +1407,17 @@ export class WorkspaceService {
});
}

public async validateImageRef(ctx: TraceContext, user: User, imageRef: string) {
public async validateImageRef(ctx: TraceContext, user: User, imageRef: string, organizationId?: string) {
try {
return await this.workspaceStarter.resolveBaseImage(ctx, user, imageRef);
return await this.workspaceStarter.resolveBaseImage(
ctx,
user,
imageRef,
undefined,
undefined,
undefined,
organizationId,
);
} catch (e) {
// see https://github.com/gitpod-io/gitpod/blob/f3e41f8d86234e4101edff2199c54f50f8cbb656/components/image-builder-mk3/pkg/orchestrator/orchestrator.go#L561
// TODO(ak) ideally we won't check a message (subject to change)
Expand All @@ -1423,8 +1431,8 @@ export class WorkspaceService {
) {
let message = details;
// strip confusing prefix
if (details.startsWith("cannt resolve base image ref: ")) {
message = details.substring("cannt resolve base image ref: ".length);
if (details.startsWith("can't resolve base image ref: ")) {
message = details.substring("can't resolve base image ref: ".length);
}
throw new ApplicationError(ErrorCodes.BAD_REQUEST, message);
}
Expand Down
10 changes: 10 additions & 0 deletions components/server/src/workspace/workspace-starter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2033,6 +2033,7 @@ export class WorkspaceStarter {
workspace?: Workspace,
instance?: WorkspaceInstance,
region?: WorkspaceRegion,
organizationId?: string,
) {
const req = new ResolveBaseImageRequest();
req.setRef(imageRef);
Expand All @@ -2041,6 +2042,15 @@ export class WorkspaceStarter {
const auth = new BuildRegistryAuth();
auth.setTotal(allowAll);
req.setAuth(auth);

// if the image resolution is for an organization, we also include the organization's set up env vars
if (organizationId) {
const envVars = await this.envVarService.listOrgEnvVarsWithValues(user.id, organizationId);

const additionalAuth = await this.getAdditionalImageAuth({ workspace: envVars });
additionalAuth.forEach((val, key) => auth.getAdditionalMap().set(key, val));
}

const client = await this.getImageBuilderClient(user, workspace, instance, region);
return client.resolveBaseImage({ span: ctx.span }, req);
}
Expand Down

0 comments on commit 77f3fde

Please sign in to comment.