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

Tool: gitpod/catfood.gitpod.cloud
  • Loading branch information
filiptronicek committed Jan 28, 2025
1 parent 12f386b commit 2cbdd47
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 9 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
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
16 changes: 15 additions & 1 deletion components/server/src/workspace/workspace-starter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
ImageBuildLogInfo,
ImageConfigFile,
NamedWorkspaceFeatureFlag,
OrgEnvVarWithValue,
Permission,
Project,
RefType,
Expand Down Expand Up @@ -128,7 +129,7 @@ import { TokenProvider } from "../user/token-provider";
import { UserAuthentication } from "../user/user-authentication";
import { ImageSourceProvider } from "./image-source-provider";
import { WorkspaceClassesConfig } from "./workspace-classes";
import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer";
import { SYSTEM_USER, SYSTEM_USER_ID, Authorizer } from "../authorization/authorizer";
import { EnvVarService, ResolvedEnvVars } from "../user/env-var-service";
import { RedlockAbortSignal } from "redlock";
import { ConfigProvider } from "./config-provider";
Expand Down Expand Up @@ -239,6 +240,7 @@ export class WorkspaceStarter {
@inject(EnvVarService) private readonly envVarService: EnvVarService,
@inject(OrganizationService) private readonly orgService: OrganizationService,
@inject(ProjectsService) private readonly projectService: ProjectsService,
@inject(Authorizer) private readonly auth: Authorizer,
) {}

public async startWorkspace(
Expand Down Expand Up @@ -2033,6 +2035,7 @@ export class WorkspaceStarter {
workspace?: Workspace,
instance?: WorkspaceInstance,
region?: WorkspaceRegion,
organizationId?: string,
) {
const req = new ResolveBaseImageRequest();
req.setRef(imageRef);
Expand All @@ -2041,6 +2044,17 @@ 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) {
await this.auth.checkPermissionOnOrganization(user.id, "read_env_var", organizationId);
const orgEnvVars = await this.orgDB.getOrgEnvironmentVariables(organizationId);
const orgEnvVarValues: OrgEnvVarWithValue[] = await this.orgDB.getOrgEnvironmentVariableValues(orgEnvVars);

const additionalAuth = await this.getAdditionalImageAuth({ workspace: orgEnvVarValues });
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 2cbdd47

Please sign in to comment.