Skip to content

Add tests for GuardedResources: Workspace, WorkspaceInstance and WorkspaceLog (1/2) #10939

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
349 changes: 348 additions & 1 deletion components/server/src/auth/resource-access.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,21 @@ import {
WorkspaceEnvVarAccessGuard,
TeamMemberResourceGuard,
GuardedWorkspace,
CompositeResourceAccessGuard,
OwnerResourceGuard,
ResourceAccessGuard,
GuardedResourceKind,
} from "./resource-access";
import { UserEnvVar } from "@gitpod/gitpod-protocol/lib/protocol";
import { User, UserEnvVar, Workspace, WorkspaceType } from "@gitpod/gitpod-protocol/lib/protocol";
import { TeamMemberInfo, TeamMemberRole, WorkspaceInstance } from "@gitpod/gitpod-protocol";

class MockedRepositoryResourceGuard implements ResourceAccessGuard {
constructor(protected response: boolean) {}

async canAccess(resource: GuardedResource, operation: ResourceAccessOp): Promise<boolean> {
return this.response;
}
}

@suite
class TestResourceAccess {
Expand Down Expand Up @@ -526,6 +539,340 @@ class TestResourceAccess {
}),
);
}

@test
public async workspaceLikeResourceGuardsCanAcccess() {
const createUser = (): User => {
return {
id: "123",
name: "testuser",
creationDate: new Date(2000, 1, 1).toISOString(),
identities: [
{
authId: "123",
authName: "testuser",
authProviderId: "github.com",
},
],
};
};
const otherUserId = "456";

const workspaceId = "ws-123";
const createWorkspace = (ownerId: string, type: WorkspaceType): Workspace => {
return {
id: workspaceId,
ownerId,
type,
config: {},
creationTime: new Date(2000, 1, 2).toISOString(),
description: "test workspace ws-123",
contextURL: "https://github.com/gitpod-io/gitpod",
context: {
title: "gitpod-io/gitpod",
normalizedContextURL: "https://github.com/gitpod-io/gitpod",
},
};
};
const createInstance = (): WorkspaceInstance => {
return {
id: "wsi-123",
workspaceId,
creationTime: new Date(2000, 1, 2).toISOString(),
region: "local",
status: {
conditions: {},
phase: "running",
},
ideUrl: "https://some.where",
workspaceImage: "gitpod/workspace-full:latest",
};
};

const tests: {
name: string;
resourceKind: GuardedResourceKind;
isOwner: boolean;
teamRole: TeamMemberRole | undefined;
workspaceType: WorkspaceType;
repositoryAccess?: boolean;
expectation: boolean;
}[] = [
// regular workspaceLog
{
name: "regular workspaceLog get owner",
resourceKind: "workspaceLog",
workspaceType: "regular",
isOwner: true,
teamRole: undefined,
expectation: true,
},
{
name: "regular workspaceLog get other",
resourceKind: "workspaceLog",
workspaceType: "regular",
isOwner: false,
teamRole: undefined,
expectation: false,
},
{
name: "regular workspaceLog get team member",
resourceKind: "workspaceLog",
workspaceType: "regular",
isOwner: false,
teamRole: "member",
expectation: false,
},
{
name: "regular workspaceLog get team owner (same as member)",
resourceKind: "workspaceLog",
workspaceType: "regular",
isOwner: false,
teamRole: "owner",
expectation: false,
},
// prebuild workspaceLog
{
name: "prebuild workspaceLog get owner",
resourceKind: "workspaceLog",
workspaceType: "prebuild",
isOwner: true,
teamRole: undefined,
expectation: true,
},
{
name: "prebuild workspaceLog get other",
resourceKind: "workspaceLog",
workspaceType: "prebuild",
isOwner: false,
teamRole: undefined,
expectation: false,
},
{
name: "prebuild workspaceLog get team member",
resourceKind: "workspaceLog",
workspaceType: "prebuild",
isOwner: false,
teamRole: "member",
expectation: true,
},
{
name: "prebuild workspaceLog get team owner (same as member)",
resourceKind: "workspaceLog",
workspaceType: "prebuild",
isOwner: false,
teamRole: "owner",
expectation: true,
},
// prebuild workspaceLog with repo access
{
name: "prebuild workspaceLog get owner with repo access",
resourceKind: "workspaceLog",
workspaceType: "prebuild",
isOwner: true,
teamRole: undefined,
repositoryAccess: true,
expectation: true,
},
{
name: "prebuild workspaceLog get other with repo access",
resourceKind: "workspaceLog",
workspaceType: "prebuild",
isOwner: false,
teamRole: undefined,
repositoryAccess: true,
expectation: true,
},
{
name: "prebuild workspaceLog get team member with repo access",
resourceKind: "workspaceLog",
workspaceType: "prebuild",
isOwner: false,
teamRole: "member",
repositoryAccess: true,
expectation: true,
},
{
name: "prebuild workspaceLog get team owner (same as member)",
resourceKind: "workspaceLog",
workspaceType: "prebuild",
isOwner: false,
teamRole: "owner",
repositoryAccess: true,
expectation: true,
},
// regular workspace
{
name: "regular workspace get owner",
resourceKind: "workspace",
workspaceType: "regular",
isOwner: true,
teamRole: undefined,
expectation: true,
},
{
name: "regular workspace get other",
resourceKind: "workspace",
workspaceType: "regular",
isOwner: false,
teamRole: undefined,
expectation: false,
},
{
name: "regular workspace get team member",
resourceKind: "workspace",
workspaceType: "regular",
isOwner: false,
teamRole: "member",
expectation: false,
},
{
name: "regular workspace get team owner (same as member)",
resourceKind: "workspace",
workspaceType: "regular",
isOwner: false,
teamRole: "owner",
expectation: false,
},
// prebuild workspace
{
name: "prebuild workspace get owner",
resourceKind: "workspace",
workspaceType: "prebuild",
isOwner: true,
teamRole: undefined,
expectation: true,
},
{
name: "prebuild workspace get other",
resourceKind: "workspace",
workspaceType: "prebuild",
isOwner: false,
teamRole: undefined,
expectation: false,
},
{
name: "prebuild workspace get team member",
resourceKind: "workspace",
workspaceType: "prebuild",
isOwner: false,
teamRole: "member",
expectation: true,
},
{
name: "prebuild workspace get team owner (same as member)",
resourceKind: "workspace",
workspaceType: "prebuild",
isOwner: false,
teamRole: "owner",
expectation: true,
},
// regular instance
{
name: "regular workspaceInstance get owner",
resourceKind: "workspaceInstance",
workspaceType: "regular",
isOwner: true,
teamRole: undefined,
expectation: true,
},
{
name: "regular workspaceInstance get other",
resourceKind: "workspaceInstance",
workspaceType: "regular",
isOwner: false,
teamRole: undefined,
expectation: false,
},
{
name: "regular workspaceInstance get team member",
resourceKind: "workspaceInstance",
workspaceType: "regular",
isOwner: false,
teamRole: "member",
expectation: false,
},
{
name: "regular workspaceInstance get team owner (same as member)",
resourceKind: "workspaceInstance",
workspaceType: "regular",
isOwner: false,
teamRole: "owner",
expectation: false,
},
// prebuild instance
{
name: "prebuild workspaceInstance get owner",
resourceKind: "workspaceInstance",
workspaceType: "prebuild",
isOwner: true,
teamRole: undefined,
expectation: true,
},
{
name: "prebuild workspaceInstance get other",
resourceKind: "workspaceInstance",
workspaceType: "prebuild",
isOwner: false,
teamRole: undefined,
expectation: false,
},
{
name: "prebuild workspaceInstance get team member",
resourceKind: "workspaceInstance",
workspaceType: "prebuild",
isOwner: false,
teamRole: "member",
expectation: true,
},
{
name: "prebuild workspaceInstance get team owner (same as member)",
resourceKind: "workspaceInstance",
workspaceType: "prebuild",
isOwner: false,
teamRole: "owner",
expectation: true,
},
];

for (const t of tests) {
const user = createUser();
const workspace = createWorkspace(t.isOwner ? user.id : otherUserId, t.workspaceType);
const resourceGuard = new CompositeResourceAccessGuard([
new OwnerResourceGuard(user.id),
new TeamMemberResourceGuard(user.id),
new MockedRepositoryResourceGuard(!!t.repositoryAccess),
]);
const teamMembers: TeamMemberInfo[] = [];
if (!!t.teamRole) {
teamMembers.push({
userId: user.id,
role: t.teamRole,
memberSince: user.creationDate,
});
}

const kind: GuardedResourceKind = "workspaceInstance";
let resource: GuardedResource | undefined = undefined;
if (kind === "workspaceInstance") {
const instance = createInstance();
resource = { kind, subject: instance, workspace, teamMembers };
} else if (kind === "workspaceLog") {
resource = { kind, subject: workspace, teamMembers };
} else if (kind === "workspace") {
resource = { kind, subject: workspace, teamMembers };
}
if (!resource) {
throw new Error("unhandled GuardedResourceKind" + kind);
}

const actual = await resourceGuard.canAccess(resource, "get");
expect(actual).to.be.eq(
t.expectation,
`"${t.name}" expected canAccess(...) === ${t.expectation}, but was ${actual}`,
);
}
}
}

module.exports = new TestResourceAccess();
20 changes: 15 additions & 5 deletions components/server/src/auth/resource-access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,16 +463,26 @@ export class RepositoryResourceGuard implements ResourceAccessGuard {
constructor(protected readonly user: User, protected readonly hostContextProvider: HostContextProvider) {}

async canAccess(resource: GuardedResource, operation: ResourceAccessOp): Promise<boolean> {
if (resource.kind !== "workspaceLog" && resource.kind !== "snapshot") {
return false;
}
// only get operations are supported
// Only get operations are supported
if (operation !== "get") {
return false;
}

// Get Workspace from GuardedResource
let workspace: Workspace;
switch (resource.kind) {
case "workspaceLog":
workspace = resource.subject;
break;
case "snapshot":
workspace = resource.workspace;
break;
default:
// We do not handle resource kinds here!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// We do not handle resource kinds here!
// We do not handle other resource kinds here!

return false;
}

// Check if user has at least read access to the repository
const workspace = resource.kind === "snapshot" ? resource.workspace : resource.subject;
const repos: Repository[] = [];
if (CommitContext.is(workspace.context)) {
repos.push(workspace.context.repository);
Expand Down
Loading