Skip to content

Commit 105793a

Browse files
default orgGitAuthProviders to true for dedicated (#17007)
* default orgGitAuthProviders to true for dedicated * restrict dupliate auth provider by host for org * remove restriction on host for orgs * add disabled select styles * update copy and clean host value better * keep constraint there for now * adding userGitAuthProviders flag
1 parent c14a25c commit 105793a

File tree

7 files changed

+82
-43
lines changed

7 files changed

+82
-43
lines changed

components/dashboard/src/contexts/FeatureFlagContext.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ const defaultFeatureFlags = {
2626
usePublicApiWorkspacesService: false,
2727
enablePersonalAccessTokens: false,
2828
oidcServiceEnabled: false,
29-
orgGitAuthProviders: false,
29+
// Default to true to enable on gitpod dedicated until ff support is added for dedicated
30+
orgGitAuthProviders: true,
31+
userGitAuthProviders: false,
3032
switchToPAYG: false,
3133
newSignupFlow: false,
3234
};
@@ -46,6 +48,7 @@ const FeatureFlagContextProvider: React.FC = ({ children }) => {
4648
const [usePublicApiWorkspacesService, setUsePublicApiWorkspacesService] = useState<boolean>(false);
4749
const [oidcServiceEnabled, setOidcServiceEnabled] = useState<boolean>(false);
4850
const [orgGitAuthProviders, setOrgGitAuthProviders] = useState<boolean>(false);
51+
const [userGitAuthProviders, setUserGitAuthProviders] = useState<boolean>(false);
4952
const [switchToPAYG, setSwitchToPAYG] = useState<boolean>(false);
5053
const [newSignupFlow, setNewSignupFlow] = useState<boolean>(false);
5154

@@ -63,7 +66,9 @@ const FeatureFlagContextProvider: React.FC = ({ children }) => {
6366
setter: setUsePublicApiWorkspacesService,
6467
},
6568
oidcServiceEnabled: { defaultValue: false, setter: setOidcServiceEnabled },
66-
orgGitAuthProviders: { defaultValue: false, setter: setOrgGitAuthProviders },
69+
// Default to true to enable on gitpod dedicated until ff support is added for dedicated
70+
orgGitAuthProviders: { defaultValue: true, setter: setOrgGitAuthProviders },
71+
userGitAuthProviders: { defaultValue: false, setter: setUserGitAuthProviders },
6772
switchToPAYG: { defaultValue: false, setter: setSwitchToPAYG },
6873
newSignupFlow: { defaultValue: false, setter: setNewSignupFlow },
6974
};
@@ -112,6 +117,7 @@ const FeatureFlagContextProvider: React.FC = ({ children }) => {
112117
usePublicApiWorkspacesService,
113118
oidcServiceEnabled,
114119
orgGitAuthProviders,
120+
userGitAuthProviders,
115121
newSignupFlow,
116122
switchToPAYG,
117123
};
@@ -126,6 +132,7 @@ const FeatureFlagContextProvider: React.FC = ({ children }) => {
126132
startWithOptions,
127133
switchToPAYG,
128134
usePublicApiWorkspacesService,
135+
userGitAuthProviders,
129136
]);
130137

131138
return <FeatureFlagContext.Provider value={flags}>{children}</FeatureFlagContext.Provider>;

components/dashboard/src/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
select.error {
111111
@apply border-gitpod-red dark:border-gitpod-red focus:border-gitpod-red dark:focus:border-gitpod-red;
112112
}
113+
select[disabled],
113114
textarea[disabled],
114115
input[disabled] {
115116
@apply bg-gray-100 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 text-gray-400 dark:text-gray-500;

components/dashboard/src/teams/git-integrations/GitIntegrationListItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const GitIntegrationListItem: FunctionComponent<Props> = ({ provider }) =
2323
const menuEntries = useMemo(() => {
2424
const result: ContextMenuEntry[] = [];
2525
result.push({
26-
title: provider.status === "verified" ? "Edit Configuration" : "Activate Integration",
26+
title: provider.status === "verified" ? "Edit" : "Activate",
2727
onClick: () => setShowEditModal(true),
2828
separator: true,
2929
});

components/dashboard/src/teams/git-integrations/GitIntegrationModal.tsx

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66

77
import { AuthProviderEntry } from "@gitpod/gitpod-protocol";
88
import { FunctionComponent, useCallback, useMemo, useState } from "react";
9-
import Alert from "../../components/Alert";
109
import { Button } from "../../components/Button";
1110
import { InputField } from "../../components/forms/InputField";
1211
import { SelectInputField } from "../../components/forms/SelectInputField";
1312
import { TextInputField } from "../../components/forms/TextInputField";
1413
import { InputWithCopy } from "../../components/InputWithCopy";
1514
import Modal, { ModalBody, ModalFooter, ModalFooterAlert, ModalHeader } from "../../components/Modal";
15+
import { Subheading } from "../../components/typography/headings";
1616
import { useInvalidateOrgAuthProvidersQuery } from "../../data/auth-providers/org-auth-providers-query";
1717
import { useUpsertOrgAuthProviderMutation } from "../../data/auth-providers/upsert-org-auth-provider-mutation";
1818
import { useCurrentOrg } from "../../data/organizations/orgs-query";
@@ -37,15 +37,14 @@ export const GitIntegrationModal: FunctionComponent<Props> = (props) => {
3737

3838
// This is a readonly value to copy and plug into external oauth config
3939
const redirectURL = useMemo(() => {
40-
// Default to an example
41-
let url = callbackUrl("gitlab.example.com");
40+
let url = "";
4241

4342
// Once it's saved, use what's stored
4443
if (!isNew) {
4544
url = savedProvider?.oauth.callBackUrl ?? url;
4645
} else {
47-
// Otherwise construct it w/ their provided host value
48-
url = callbackUrl(host);
46+
// Otherwise construct it w/ their provided host value or example
47+
url = callbackUrl(host || "gitlab.example.com");
4948
}
5049

5150
return url;
@@ -79,9 +78,7 @@ export const GitIntegrationModal: FunctionComponent<Props> = (props) => {
7978
const hostOnBlur = useCallback(() => {
8079
hostOnBlurErrorTracking();
8180

82-
if (host.startsWith("https://")) {
83-
setHost(host.replace("https://", ""));
84-
}
81+
setHost(cleanHost(host));
8582
}, [host, hostOnBlurErrorTracking]);
8683

8784
// TODO: We could remove this extra state management if we convert the modal into a detail flow w/ it's own route
@@ -116,7 +113,7 @@ export const GitIntegrationModal: FunctionComponent<Props> = (props) => {
116113
const newProvider = await upsertProvider.mutateAsync({
117114
provider: isNew
118115
? {
119-
host: host.replace("https://", ""),
116+
host: cleanHost(host),
120117
type,
121118
clientId: trimmedId,
122119
clientSecret: trimmedSecret,
@@ -192,20 +189,20 @@ export const GitIntegrationModal: FunctionComponent<Props> = (props) => {
192189
return false;
193190
}}
194191
>
195-
<ModalHeader>{isNew ? "New Git Integration" : "Git Integration"}</ModalHeader>
192+
<ModalHeader>{isNew ? "New Git Auth" : "Git Auth"}</ModalHeader>
196193
<ModalBody>
197-
<div className="flex flex-col text-gray-500">
198-
Configure an integration with a self-managed instance of GitLab, GitHub, or Bitbucket.
199-
</div>
194+
{isNew && (
195+
<Subheading>
196+
Configure Git Auth with a self-managed instance of GitLab, GitHub or Bitbucket Server.
197+
</Subheading>
198+
)}
200199

201200
<div>
202-
{isNew && (
203-
<SelectInputField label="Provider Type" value={type} onChange={setType}>
204-
<option value="GitHub">GitHub</option>
205-
<option value="GitLab">GitLab</option>
206-
<option value="BitbucketServer">Bitbucket Server</option>
207-
</SelectInputField>
208-
)}
201+
<SelectInputField disabled={!isNew} label="Provider Type" value={type} onChange={setType}>
202+
<option value="GitHub">GitHub</option>
203+
<option value="GitLab">GitLab</option>
204+
<option value="BitbucketServer">Bitbucket Server</option>
205+
</SelectInputField>
209206
<TextInputField
210207
label="Provider Host Name"
211208
value={host}
@@ -247,7 +244,7 @@ export const GitIntegrationModal: FunctionComponent<Props> = (props) => {
247244
!isNew &&
248245
savedProvider?.status !== "verified" && (
249246
<ModalFooterAlert type="warning" closable={false}>
250-
You need to activate this integration.
247+
You need to activate this configuration.
251248
</ModalFooterAlert>
252249
)
253250
)}
@@ -258,7 +255,7 @@ export const GitIntegrationModal: FunctionComponent<Props> = (props) => {
258255
Cancel
259256
</Button>
260257
<Button onClick={activate} disabled={!isValid || savingProvider} loading={savingProvider}>
261-
Activate Integration
258+
Activate
262259
</Button>
263260
</ModalFooter>
264261
</Modal>
@@ -331,3 +328,17 @@ const RedirectUrlDescription: FunctionComponent<RedirectUrlDescriptionProps> = (
331328
</span>
332329
);
333330
};
331+
332+
function cleanHost(host: string) {
333+
let cleanedHost = host;
334+
335+
// Removing https protocol
336+
if (host.startsWith("https://")) {
337+
cleanedHost = host.replace("https://", "");
338+
}
339+
340+
// Trim any trailing slashes
341+
cleanedHost = cleanedHost.replace(/\/$/, "");
342+
343+
return cleanedHost;
344+
}

components/dashboard/src/teams/git-integrations/GitIntegrationsList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const GitIntegrationsList: FunctionComponent<Props> = ({ providers }) =>
3333
{providers.length !== 0 ? (
3434
<div className="">
3535
<Button className="whitespace-nowrap" onClick={onCreate}>
36-
New Integration
36+
New Git Auth
3737
</Button>
3838
</div>
3939
) : null}
@@ -43,7 +43,7 @@ export const GitIntegrationsList: FunctionComponent<Props> = ({ providers }) =>
4343
<EmptyMessage
4444
title="No Git Auth configurations"
4545
subtitle="Configure Git Auth with GitHub or GitLab."
46-
buttonText="New Integration"
46+
buttonText="New Git Auth"
4747
onClick={onCreate}
4848
/>
4949
) : (

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

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@
66

77
import { AuthProviderEntry, AuthProviderInfo } from "@gitpod/gitpod-protocol";
88
import { SelectAccountPayload } from "@gitpod/gitpod-protocol/lib/auth";
9+
import { useQuery } from "@tanstack/react-query";
910
import React, { useCallback, useContext, useEffect, useState } from "react";
1011
import Alert from "../components/Alert";
1112
import CheckBox from "../components/CheckBox";
1213
import ConfirmationModal from "../components/ConfirmationModal";
1314
import { ContextMenuEntry } from "../components/ContextMenu";
15+
import { Delayed } from "../components/Delayed";
1416
import InfoBox from "../components/InfoBox";
1517
import { ItemsList } from "../components/ItemsList";
18+
import { SpinnerLoader } from "../components/Loader";
1619
import Modal, { ModalBody, ModalHeader, ModalFooter } from "../components/Modal";
1720
import { Heading2, Subheading } from "../components/typography/headings";
21+
import { useFeatureFlags } from "../contexts/FeatureFlagContext";
1822
import copy from "../images/copy.svg";
1923
import exclamation from "../images/exclamation.svg";
2024
import { openAuthorizeWindow } from "../provider-utils";
@@ -294,7 +298,6 @@ function GitProviders() {
294298
)}
295299

296300
{editModal && (
297-
// TODO: Use title and buttons props
298301
<Modal visible={true} onClose={() => setEditModal(undefined)}>
299302
<ModalHeader>Edit Permissions</ModalHeader>
300303
<ModalBody>
@@ -340,6 +343,7 @@ function GitProviders() {
340343
{authProviders &&
341344
authProviders.map((ap) => (
342345
<AuthEntryItem
346+
key={ap.authProviderId}
343347
isConnected={isConnected}
344348
gitProviderMenu={gitProviderMenu}
345349
getUsername={getUsername}
@@ -354,8 +358,7 @@ function GitProviders() {
354358

355359
function GitIntegrations() {
356360
const { user } = useContext(UserContext);
357-
358-
const [providers, setProviders] = useState<AuthProviderEntry[]>([]);
361+
const { userGitAuthProviders } = useFeatureFlags();
359362

360363
const [modal, setModal] = useState<
361364
| { mode: "new" }
@@ -364,13 +367,15 @@ function GitIntegrations() {
364367
| undefined
365368
>(undefined);
366369

367-
useEffect(() => {
368-
updateOwnAuthProviders();
369-
}, []);
370-
371-
const updateOwnAuthProviders = async () => {
372-
setProviders(await getGitpodService().server.getOwnAuthProviders());
373-
};
370+
const {
371+
data: providers,
372+
isLoading,
373+
refetch,
374+
} = useQuery(
375+
["own-auth-providers", { userId: user?.id ?? "" }],
376+
async () => await getGitpodService().server.getOwnAuthProviders(),
377+
{ enabled: !!user },
378+
);
374379

375380
const deleteProvider = async (provider: AuthProviderEntry) => {
376381
try {
@@ -379,7 +384,7 @@ function GitIntegrations() {
379384
console.log(error);
380385
}
381386
setModal(undefined);
382-
updateOwnAuthProviders();
387+
refetch();
383388
};
384389

385390
const gitProviderMenu = (provider: AuthProviderEntry) => {
@@ -397,14 +402,28 @@ function GitIntegrations() {
397402
return result;
398403
};
399404

405+
if (isLoading) {
406+
return (
407+
<Delayed>
408+
<SpinnerLoader />
409+
</Delayed>
410+
);
411+
}
412+
413+
// If user has no personal providers and ff is not enabled, don't show anything
414+
// Otherwise we show their existing providers w/o ability to create new ones if ff is disabled
415+
if ((providers || []).length === 0 && !userGitAuthProviders) {
416+
return null;
417+
}
418+
400419
return (
401420
<div>
402421
{modal?.mode === "new" && (
403422
<GitIntegrationModal
404423
mode={modal.mode}
405424
userId={user?.id || "no-user"}
406425
onClose={() => setModal(undefined)}
407-
onUpdate={updateOwnAuthProviders}
426+
onUpdate={refetch}
408427
/>
409428
)}
410429
{modal?.mode === "edit" && (
@@ -413,7 +432,7 @@ function GitIntegrations() {
413432
userId={user?.id || "no-user"}
414433
provider={modal.provider}
415434
onClose={() => setModal(undefined)}
416-
onUpdate={updateOwnAuthProviders}
435+
onUpdate={refetch}
417436
/>
418437
)}
419438
{modal?.mode === "delete" && (
@@ -437,7 +456,8 @@ function GitIntegrations() {
437456
Manage Git integrations for self-managed instances of GitLab, GitHub, or Bitbucket.
438457
</Subheading>
439458
</div>
440-
{providers.length !== 0 ? (
459+
{/* Hide create button if ff is disabled */}
460+
{userGitAuthProviders && (providers || []).length !== 0 ? (
441461
<div className="mt-3 flex mt-0">
442462
<button onClick={() => setModal({ mode: "new" })} className="ml-2">
443463
New Integration

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ export class AuthProviderService {
123123
}
124124

125125
async createOrgAuthProvider(entry: AuthProviderEntry.NewOrgEntry): Promise<AuthProviderEntry> {
126-
// TODO: only restrict existing provider w/ same host if it's the same organization
127-
const existing = await this.authProviderDB.findByHost(entry.host);
126+
const orgProviders = await this.authProviderDB.findByOrgId(entry.organizationId);
127+
const existing = orgProviders.find((p) => p.host === entry.host);
128128
if (existing) {
129129
throw new Error("Provider for this host already exists.");
130130
}

0 commit comments

Comments
 (0)