Skip to content

Commit 35f53b9

Browse files
Enforce minimum scopes in the UI (#20217)
1 parent 4b05e71 commit 35f53b9

24 files changed

+155
-291
lines changed

components/dashboard/src/user-settings/Integrations.tsx

+58-53
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { getScopesForAuthProviderType } from "@gitpod/public-api-common/lib/auth-providers";
7+
import { getRequiredScopes, getScopesForAuthProviderType } from "@gitpod/public-api-common/lib/auth-providers";
88
import { SelectAccountPayload } from "@gitpod/gitpod-protocol/lib/auth";
99
import { useQuery } from "@tanstack/react-query";
10-
import React, { useCallback, useContext, useEffect, useMemo, useState } from "react";
10+
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
1111
import Alert from "../components/Alert";
1212
import { CheckboxInputField, CheckboxListField } from "../components/forms/CheckboxInputField";
1313
import ConfirmationModal from "../components/ConfirmationModal";
@@ -54,6 +54,46 @@ export default function Integrations() {
5454
);
5555
}
5656

57+
const getDescriptionForScope = (scope: string) => {
58+
switch (scope) {
59+
// GitHub
60+
case "user:email":
61+
return "Read-only access to your email addresses";
62+
case "read:user":
63+
return "Read-only access to your profile information";
64+
case "public_repo":
65+
return "Write access to code in public repositories and organizations";
66+
case "repo":
67+
return "Read/write access to code in private repositories and organizations";
68+
case "read:org":
69+
return "Read-only access to organizations (used to suggest organizations when forking a repository)";
70+
case "workflow":
71+
return "Allow updating GitHub Actions workflow files";
72+
// GitLab
73+
case "read_user":
74+
return "Read-only access to your email addresses";
75+
case "api":
76+
return "Allow making API calls (used to set up a webhook when enabling prebuilds for a repository)";
77+
case "read_repository":
78+
return "Read/write access to your repositories";
79+
// Bitbucket
80+
case "account":
81+
return "Read-only access to your account information";
82+
case "repository":
83+
return "Read-only access to your repositories (note: Bitbucket doesn't support revoking scopes)";
84+
case "repository:write":
85+
return "Read/write access to your repositories (note: Bitbucket doesn't support revoking scopes)";
86+
case "pullrequest":
87+
return "Read access to pull requests and ability to collaborate via comments, tasks, and approvals (note: Bitbucket doesn't support revoking scopes)";
88+
case "pullrequest:write":
89+
return "Allow creating, merging and declining pull requests (note: Bitbucket doesn't support revoking scopes)";
90+
case "webhook":
91+
return "Allow installing webhooks (used when enabling prebuilds for a repository, note: Bitbucket doesn't support revoking scopes)";
92+
default:
93+
return "";
94+
}
95+
};
96+
5797
function GitProviders() {
5898
const { user, setUser } = useContext(UserContext);
5999

@@ -250,45 +290,6 @@ function GitProviders() {
250290
setEditModal({ ...editModal, nextScopes });
251291
};
252292

253-
const getDescriptionForScope = (scope: string) => {
254-
switch (scope) {
255-
case "user:email":
256-
return "Read-only access to your email addresses";
257-
case "read:user":
258-
return "Read-only access to your profile information";
259-
case "public_repo":
260-
return "Write access to code in public repositories and organizations";
261-
case "repo":
262-
return "Read/write access to code in private repositories and organizations";
263-
case "read:org":
264-
return "Read-only access to organizations (used to suggest organizations when forking a repository)";
265-
case "workflow":
266-
return "Allow updating GitHub Actions workflow files";
267-
// GitLab
268-
case "read_user":
269-
return "Read-only access to your email addresses";
270-
case "api":
271-
return "Allow making API calls (used to set up a webhook when enabling prebuilds for a repository)";
272-
case "read_repository":
273-
return "Read/write access to your repositories";
274-
// Bitbucket
275-
case "account":
276-
return "Read-only access to your account information";
277-
case "repository":
278-
return "Read-only access to your repositories (note: Bitbucket doesn't support revoking scopes)";
279-
case "repository:write":
280-
return "Read/write access to your repositories (note: Bitbucket doesn't support revoking scopes)";
281-
case "pullrequest":
282-
return "Read access to pull requests and ability to collaborate via comments, tasks, and approvals (note: Bitbucket doesn't support revoking scopes)";
283-
case "pullrequest:write":
284-
return "Allow creating, merging and declining pull requests (note: Bitbucket doesn't support revoking scopes)";
285-
case "webhook":
286-
return "Allow installing webhooks (used when enabling prebuilds for a repository, note: Bitbucket doesn't support revoking scopes)";
287-
default:
288-
return "";
289-
}
290-
};
291-
292293
return (
293294
<div>
294295
{selectAccountModal && (
@@ -325,18 +326,22 @@ function GitProviders() {
325326
<ModalHeader>Edit Permissions</ModalHeader>
326327
<ModalBody>
327328
<CheckboxListField label="Configure provider permissions.">
328-
{(getScopesForAuthProviderType(editModal.provider.type) || []).map((scope) => (
329-
<CheckboxInputField
330-
key={scope}
331-
value={scope}
332-
label={scope}
333-
hint={getDescriptionForScope(scope)}
334-
checked={editModal.nextScopes.has(scope)}
335-
// disabled={editModal.provider.requirements?.default.includes(scope)} // what?!
336-
topMargin={false}
337-
onChange={(checked) => onChangeScopeHandler(checked, scope)}
338-
/>
339-
))}
329+
{(getScopesForAuthProviderType(editModal.provider.type) || []).map((scope) => {
330+
const isRequired = getRequiredScopes(editModal.provider.type)?.default.includes(scope);
331+
332+
return (
333+
<CheckboxInputField
334+
key={scope}
335+
value={scope}
336+
label={scope + (isRequired ? " (required)" : "")}
337+
hint={getDescriptionForScope(scope)}
338+
checked={editModal.nextScopes.has(scope)}
339+
disabled={isRequired}
340+
topMargin={false}
341+
onChange={(checked) => onChangeScopeHandler(checked, scope)}
342+
/>
343+
);
344+
})}
340345
</CheckboxListField>
341346
</ModalBody>
342347
<ModalFooter>

components/gitpod-db/src/typeorm/user-db-impl.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ export class TypeORMUserDBImpl extends TransactionalDBImpl<UserDB> implements Us
571571
accessTokenExpiresAt: expiry,
572572
client,
573573
user,
574-
scopes: scopes,
574+
scopes,
575575
};
576576
}
577577
async issueRefreshToken(accessToken: OAuthToken): Promise<OAuthToken> {

components/public-api/typescript-common/src/auth-providers.ts

+50-34
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,25 @@
66

77
import { AuthProviderType } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";
88

9-
export namespace GitLabScope {
9+
export namespace GitLabOAuthScopes {
1010
export const READ_USER = "read_user";
1111
export const API = "api";
1212
export const READ_REPO = "read_repository";
1313

1414
export const ALL = [READ_USER, API, READ_REPO];
15-
/**
16-
* Minimal required permission.
17-
* GitLab API usage requires the permission of a user.
18-
*/
19-
export const DEFAULT = [READ_USER, API];
20-
export const REPO = [API, READ_REPO];
15+
16+
export const Requirements = {
17+
/**
18+
* Minimal required permission.
19+
* GitLab API usage requires the permission of a user.
20+
*/
21+
DEFAULT: [READ_USER, API],
22+
23+
REPO: [API, READ_REPO],
24+
};
2125
}
2226

23-
export namespace GitHubScope {
27+
export namespace GitHubOAuthScopes {
2428
export const EMAIL = "user:email";
2529
export const READ_USER = "read:user";
2630
export const PUBLIC = "public_repo";
@@ -29,9 +33,17 @@ export namespace GitHubScope {
2933
export const WORKFLOW = "workflow";
3034

3135
export const ALL = [EMAIL, READ_USER, PUBLIC, PRIVATE, ORGS, WORKFLOW];
32-
export const DEFAULT = ALL;
33-
export const PUBLIC_REPO = ALL;
34-
export const PRIVATE_REPO = ALL;
36+
37+
export const Requirements = {
38+
/**
39+
* Minimal required permission.
40+
* GitHub's API is not restricted any further.
41+
*/
42+
DEFAULT: [EMAIL],
43+
44+
PUBLIC_REPO: [PUBLIC],
45+
PRIVATE_REPO: [PRIVATE],
46+
};
3547
}
3648

3749
export namespace BitbucketOAuthScopes {
@@ -48,15 +60,14 @@ export namespace BitbucketOAuthScopes {
4860
/** Create, comment and merge pull requests */
4961
export const PULL_REQUEST_WRITE = "pullrequest:write";
5062

51-
export const ALL = [
52-
ACCOUNT_READ,
53-
REPOSITORY_READ,
54-
REPOSITORY_WRITE,
55-
PULL_REQUEST_READ,
56-
PULL_REQUEST_WRITE,
57-
];
63+
export const ALL = [ACCOUNT_READ, REPOSITORY_READ, REPOSITORY_WRITE, PULL_REQUEST_READ, PULL_REQUEST_WRITE];
5864

59-
export const DEFAULT = ALL;
65+
export const Requirements = {
66+
/**
67+
* Minimal required permission.
68+
*/
69+
DEFAULT: ALL,
70+
};
6071
}
6172

6273
export namespace BitbucketServerOAuthScopes {
@@ -74,17 +85,22 @@ export namespace BitbucketServerOAuthScopes {
7485

7586
export const ALL = [PUBLIC_REPOS, REPO_READ, REPO_WRITE, REPO_ADMIN, PROJECT_ADMIN];
7687

77-
export const DEFAULT = ALL;
88+
export const Requirements = {
89+
/**
90+
* Minimal required permission.
91+
*/
92+
DEFAULT: [PUBLIC_REPOS, REPO_READ, REPO_WRITE],
93+
};
7894
}
7995

8096
export function getScopesForAuthProviderType(type: AuthProviderType | string) {
8197
switch (type) {
8298
case AuthProviderType.GITHUB:
8399
case "GitHub":
84-
return GitHubScope.ALL;
100+
return GitHubOAuthScopes.ALL;
85101
case AuthProviderType.GITLAB:
86102
case "GitLab":
87-
return GitLabScope.ALL;
103+
return GitLabOAuthScopes.ALL;
88104
case AuthProviderType.BITBUCKET:
89105
case "Bitbucket":
90106
return BitbucketOAuthScopes.ALL;
@@ -99,30 +115,30 @@ export function getRequiredScopes(type: AuthProviderType | string) {
99115
case AuthProviderType.GITHUB:
100116
case "GitHub":
101117
return {
102-
default: GitHubScope.DEFAULT,
103-
publicRepo: GitHubScope.PUBLIC_REPO,
104-
privateRepo: GitHubScope.PRIVATE_REPO,
118+
default: GitHubOAuthScopes.Requirements.DEFAULT,
119+
publicRepo: GitHubOAuthScopes.Requirements.PUBLIC_REPO,
120+
privateRepo: GitHubOAuthScopes.Requirements.PRIVATE_REPO,
105121
};
106122
case AuthProviderType.GITLAB:
107123
case "GitLab":
108124
return {
109-
default: GitLabScope.DEFAULT,
110-
publicRepo: GitLabScope.DEFAULT,
111-
privateRepo: GitLabScope.REPO,
125+
default: GitLabOAuthScopes.Requirements.DEFAULT,
126+
publicRepo: GitLabOAuthScopes.Requirements.DEFAULT,
127+
privateRepo: GitLabOAuthScopes.Requirements.REPO,
112128
};
113129
case AuthProviderType.BITBUCKET:
114130
case "Bitbucket":
115131
return {
116-
default: BitbucketOAuthScopes.DEFAULT,
117-
publicRepo: BitbucketOAuthScopes.DEFAULT,
118-
privateRepo: BitbucketOAuthScopes.DEFAULT,
132+
default: BitbucketOAuthScopes.Requirements.DEFAULT,
133+
publicRepo: BitbucketOAuthScopes.Requirements.DEFAULT,
134+
privateRepo: BitbucketOAuthScopes.Requirements.DEFAULT,
119135
};
120136
case AuthProviderType.BITBUCKET_SERVER:
121137
case "BitbucketServer":
122138
return {
123-
default: BitbucketServerOAuthScopes.DEFAULT,
124-
publicRepo: BitbucketServerOAuthScopes.DEFAULT,
125-
privateRepo: BitbucketServerOAuthScopes.DEFAULT,
139+
default: BitbucketServerOAuthScopes.Requirements.DEFAULT,
140+
publicRepo: BitbucketServerOAuthScopes.Requirements.DEFAULT,
141+
privateRepo: BitbucketServerOAuthScopes.Requirements.DEFAULT,
126142
};
127143
}
128144
}

components/server/src/auth/auth-provider-scopes.ts

-51
This file was deleted.

components/server/src/auth/auth-provider-service.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
1818
import fetch from "node-fetch";
1919
import { Authorizer } from "../authorization/authorizer";
2020
import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
21-
import { getRequiredScopes, getScopesOfProvider } from "./auth-provider-scopes";
21+
import { getRequiredScopes, getScopesForAuthProviderType } from "@gitpod/public-api-common/lib/auth-providers";
2222

2323
@injectable()
2424
export class AuthProviderService {
@@ -106,8 +106,8 @@ export class AuthProviderService {
106106
hiddenOnDashboard: ap.hiddenOnDashboard,
107107
disallowLogin: ap.disallowLogin,
108108
description: ap.description,
109-
scopes: getScopesOfProvider(ap),
110-
requirements: getRequiredScopes(ap),
109+
scopes: getScopesForAuthProviderType(ap.type),
110+
requirements: getRequiredScopes(ap.type),
111111
};
112112
}
113113

components/server/src/auth/resource-access.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import { UnauthorizedError } from "../errors";
2323
import { RepoURL } from "../repohost";
2424
import { HostContextProvider } from "./host-context-provider";
2525
import { reportGuardAccessCheck } from "../prometheus-metrics";
26-
import { getRequiredScopes } from "./auth-provider-scopes";
2726
import { FunctionAccessGuard } from "./function-access";
27+
import { getRequiredScopes } from "@gitpod/public-api-common/lib/auth-providers";
2828

2929
declare let resourceInstance: GuardedResource;
3030
export type GuardedResourceKind = typeof resourceInstance.kind;
@@ -585,7 +585,7 @@ export class RepositoryResourceGuard implements ResourceAccessGuard {
585585
const identity = User.getIdentity(this.user, authProvider.authProviderId);
586586
if (!identity) {
587587
const providerType = authProvider.info.authProviderType;
588-
const requiredScopes = getRequiredScopes({ type: providerType })?.default;
588+
const requiredScopes = getRequiredScopes(providerType)?.default;
589589
throw UnauthorizedError.create({
590590
host: repoUrl.host,
591591
repoName: repoUrl.repo,

components/server/src/bitbucket-server/bitbucket-server-auth-provider.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import express from "express";
1010
import { inject, injectable } from "inversify";
1111
import { AuthUserSetup } from "../auth/auth-provider";
1212
import { GenericAuthProvider } from "../auth/generic-auth-provider";
13-
import { BitbucketServerOAuthScopes } from "./bitbucket-server-oauth-scopes";
1413
import { BitbucketServerApi } from "./bitbucket-server-api";
14+
import { BitbucketServerOAuthScopes } from "@gitpod/public-api-common/lib/auth-providers";
1515

1616
@injectable()
1717
export class BitbucketServerAuthProvider extends GenericAuthProvider {

0 commit comments

Comments
 (0)