Skip to content

Commit

Permalink
Use AuthProviderService API in Dashboard – EXP-847 (#19057)
Browse files Browse the repository at this point in the history
* convert Login.ts

* extract auth provider utils into protocol

* complete migration from useAuthProviders to  useAuthProviderDescriptions

* fixup import of scopes util

* revert rename

* migrate useDeleteOrgAuthProviderMutation

* migrate Org Git Integrations

* fix label for authprovider types

* wip migration user-level git integrations

* WIP updateProviderEntry

* wip migration

* fixup: label of auth provider types

* fixup: dont render Unknown type

* fix label

* fix this undefined

* bump CACHE_VERSION

* fixup allow to change the `clientId` only

* fixup add AuthProviderClasses to supported messages

---------

Co-authored-by: Huiwen <[email protected]>
  • Loading branch information
AlexTugarev and mustard-mh authored Nov 16, 2023
1 parent bde844b commit 21bb3c1
Show file tree
Hide file tree
Showing 33 changed files with 827 additions and 304 deletions.
12 changes: 6 additions & 6 deletions components/dashboard/src/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* See License.AGPL.txt in the project root for license information.
*/

import { AuthProviderInfo } from "@gitpod/gitpod-protocol";
import * as GitpodCookie from "@gitpod/gitpod-protocol/lib/util/gitpod-cookie";
import { useContext, useEffect, useState, useMemo, useCallback, FC } from "react";
import { UserContext } from "./user-context";
Expand All @@ -18,9 +17,10 @@ import { getURLHash } from "./utils";
import ErrorMessage from "./components/ErrorMessage";
import { Heading1, Heading2, Subheading } from "./components/typography/headings";
import { SSOLoginForm } from "./login/SSOLoginForm";
import { useAuthProviders } from "./data/auth-providers/auth-provider-query";
import { useAuthProviderDescriptions } from "./data/auth-providers/auth-provider-descriptions-query";
import { SetupPending } from "./login/SetupPending";
import { useNeedsSetup } from "./dedicated-setup/use-needs-setup";
import { AuthProviderDescription } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

export function markLoggedIn() {
document.cookie = GitpodCookie.generateCookie(window.location.hostname);
Expand All @@ -38,7 +38,7 @@ export const Login: FC<LoginProps> = ({ onLoggedIn }) => {

const urlHash = useMemo(() => getURLHash(), []);

const authProviders = useAuthProviders();
const authProviders = useAuthProviderDescriptions();
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
const [hostFromContext, setHostFromContext] = useState<string | undefined>();
const [repoPathname, setRepoPathname] = useState<string | undefined>();
Expand All @@ -58,7 +58,7 @@ export const Login: FC<LoginProps> = ({ onLoggedIn }) => {
}
}, [urlHash]);

let providerFromContext: AuthProviderInfo | undefined;
let providerFromContext: AuthProviderDescription | undefined;
if (hostFromContext && authProviders.data) {
providerFromContext = authProviders.data.find((provider) => provider.host === hostFromContext);
}
Expand Down Expand Up @@ -158,7 +158,7 @@ export const Login: FC<LoginProps> = ({ onLoggedIn }) => {
className="btn-login flex-none w-56 h-10 p-0 inline-flex rounded-xl"
onClick={() => openLogin(providerFromContext!.host)}
>
{iconForAuthProvider(providerFromContext.authProviderType)}
{iconForAuthProvider(providerFromContext.type)}
<span className="pt-2 pb-2 mr-3 text-sm my-auto font-medium truncate overflow-ellipsis">
Continue with {simplifyProviderName(providerFromContext.host)}
</span>
Expand All @@ -170,7 +170,7 @@ export const Login: FC<LoginProps> = ({ onLoggedIn }) => {
className="btn-login flex-none w-56 h-10 p-0 inline-flex rounded-xl"
onClick={() => openLogin(ap.host)}
>
{iconForAuthProvider(ap.authProviderType)}
{iconForAuthProvider(ap.type)}
<span className="pt-2 pb-2 mr-3 text-sm my-auto font-medium truncate overflow-ellipsis">
Continue with {simplifyProviderName(ap.host)}
</span>
Expand Down
23 changes: 10 additions & 13 deletions components/dashboard/src/components/AuthorizeGit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
* See License.AGPL.txt in the project root for license information.
*/

import { AuthProviderInfo } from "@gitpod/gitpod-protocol";
import { FC, useCallback, useContext } from "react";
import { Link } from "react-router-dom";
import { useAuthProviders } from "../data/auth-providers/auth-provider-query";
import { useAuthProviderDescriptions } from "../data/auth-providers/auth-provider-descriptions-query";
import { openAuthorizeWindow } from "../provider-utils";
import { getGitpodService } from "../service/service";
import { UserContext, useCurrentUser } from "../user-context";
Expand All @@ -16,45 +15,43 @@ import { Heading2, Heading3, Subheading } from "./typography/headings";
import classNames from "classnames";
import { iconForAuthProvider, simplifyProviderName } from "../provider-utils";
import { useIsOwner } from "../data/organizations/members-query";
import { AuthProviderDescription } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

export function useNeedsGitAuthorization() {
const authProviders = useAuthProviders();
const authProviders = useAuthProviderDescriptions();
const user = useCurrentUser();
if (!user || !authProviders.data) {
return false;
}
return !authProviders.data.some((ap) => user.identities.some((i) => ap.authProviderId === i.authProviderId));
return !authProviders.data.some((ap) => user.identities.some((i) => ap.id === i.authProviderId));
}

export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
const { setUser } = useContext(UserContext);
const owner = useIsOwner();
const authProviders = useAuthProviders();
const { data: authProviders } = useAuthProviderDescriptions();
const updateUser = useCallback(() => {
getGitpodService().server.getLoggedInUser().then(setUser);
}, [setUser]);

const connect = useCallback(
(ap: AuthProviderInfo) => {
(ap: AuthProviderDescription) => {
openAuthorizeWindow({
host: ap.host,
scopes: ap.requirements?.default,
overrideScopes: true,
onSuccess: updateUser,
});
},
[updateUser],
);

if (authProviders.data === undefined) {
if (authProviders === undefined) {
return <></>;
}

const verifiedProviders = authProviders.data.filter((ap) => ap.verified);

return (
<div className={classNames("text-center p-4 m-4 py-10", className)}>
{verifiedProviders.length === 0 ? (
{authProviders.length === 0 ? (
<>
<Heading3 className="pb-2">No Git integrations</Heading3>
{!!owner ? (
Expand Down Expand Up @@ -82,7 +79,7 @@ export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
Select one of the following available providers to access repositories for your account.
</Subheading>
<div className="flex flex-col items-center">
{verifiedProviders.map((ap) => {
{authProviders.map((ap) => {
return (
<Button
onClick={() => connect(ap)}
Expand All @@ -91,7 +88,7 @@ export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
className="mt-3 btn-login flex-none w-56 px-0 py-0.5 inline-flex"
>
<div className="flex relative -left-4 w-56">
{iconForAuthProvider(ap.authProviderType)}
{iconForAuthProvider(ap.type)}
<span className="pt-2 pb-2 mr-3 text-sm my-auto font-medium truncate overflow-ellipsis">
Continue with {simplifyProviderName(ap.host)}
</span>
Expand Down
10 changes: 7 additions & 3 deletions components/dashboard/src/components/RepositoryFinder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { ReactComponent as RepositoryIcon } from "../icons/RepositoryWithColor.s
import { SuggestedRepository } from "@gitpod/gitpod-protocol";
import { MiddleDot } from "./typography/MiddleDot";
import { useUnifiedRepositorySearch } from "../data/git-providers/unified-repositories-search-query";
import { useAuthProviders } from "../data/auth-providers/auth-provider-query";
import { useAuthProviderDescriptions } from "../data/auth-providers/auth-provider-descriptions-query";
import { ReactComponent as Exclamation2 } from "../images/exclamation2.svg";
import { AuthProviderType } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

interface RepositoryFinderProps {
selectedContextURL?: string;
Expand All @@ -39,7 +40,7 @@ export default function RepositoryFinder({
hasMore,
} = useUnifiedRepositorySearch({ searchString, excludeProjects });

const authProviders = useAuthProviders();
const authProviders = useAuthProviderDescriptions();

const handleSelectionChange = useCallback(
(selectedID: string) => {
Expand Down Expand Up @@ -115,7 +116,10 @@ export default function RepositoryFinder({
isSelectable: false,
} as ComboboxElement);
}
if (searchString.length >= 3 && authProviders.data?.some((p) => p.authProviderType === "BitbucketServer")) {
if (
searchString.length >= 3 &&
authProviders.data?.some((p) => p.type === AuthProviderType.BITBUCKET_SERVER)
) {
// add an element that tells the user that the Bitbucket Server does only support prefix search
result.push({
id: "bitbucket-server",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License.AGPL.txt in the project root for license information.
*/

import { useQuery } from "@tanstack/react-query";
import { authProviderClient } from "../../service/public-api";
import { useCurrentUser } from "../../user-context";
import {
AuthProviderDescription,
ListAuthProviderDescriptionsRequest,
} from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

export const useAuthProviderDescriptions = () => {
const user = useCurrentUser();
const query = useQuery<AuthProviderDescription[]>({
queryKey: getAuthProviderDescriptionsQueryKey(),
queryFn: async () => {
const params = new ListAuthProviderDescriptionsRequest();
if (user) {
params.id = {
case: "userId",
value: user.id,
};
}
const response = await authProviderClient.listAuthProviderDescriptions(params);
return response.descriptions;
},
});
return query;
};

export const getAuthProviderDescriptionsQueryKey = () => ["auth-provider-descriptions", {}];

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License.AGPL.txt in the project root for license information.
*/

import { useMutation, useQueryClient } from "@tanstack/react-query";
import { getOrgAuthProvidersQueryKey } from "./org-auth-providers-query";
import { CreateAuthProviderRequest } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";
import { authProviderClient } from "../../service/public-api";

type CreateAuthProviderArgs = {
provider: Pick<CreateAuthProviderRequest, "host" | "type"> & {
clientId: string;
clientSecret: string;
orgId: string;
};
};
export const useCreateOrgAuthProviderMutation = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async ({ provider }: CreateAuthProviderArgs) => {
const response = await authProviderClient.createAuthProvider(
new CreateAuthProviderRequest({
owner: { case: "organizationId", value: provider.orgId },
host: provider.host,
oauth2Config: {
clientId: provider.clientId,
clientSecret: provider.clientSecret,
},
type: provider.type,
}),
);
return response.authProvider!;
},
onSuccess(provider) {
const orgId = provider?.owner?.value;
if (!orgId) {
return;
}

queryClient.invalidateQueries({ queryKey: getOrgAuthProvidersQueryKey(orgId) });
},
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License.AGPL.txt in the project root for license information.
*/

import { useMutation, useQueryClient } from "@tanstack/react-query";
import { CreateAuthProviderRequest } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";
import { authProviderClient } from "../../service/public-api";
import { getUserAuthProvidersQueryKey } from "./user-auth-providers-query";

type CreateAuthProviderArgs = {
provider: Pick<CreateAuthProviderRequest, "host" | "type"> & {
clientId: string;
clientSecret: string;
userId: string;
};
};
export const useCreateUserAuthProviderMutation = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async ({ provider }: CreateAuthProviderArgs) => {
const response = await authProviderClient.createAuthProvider(
new CreateAuthProviderRequest({
owner: { case: "ownerId", value: provider.userId },
host: provider.host,
oauth2Config: {
clientId: provider.clientId,
clientSecret: provider.clientSecret,
},
type: provider.type,
}),
);
return response.authProvider!;
},
onSuccess(provider) {
const userId = provider?.owner?.value;
if (!userId) {
return;
}

queryClient.invalidateQueries({ queryKey: getUserAuthProvidersQueryKey(userId) });
},
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
*/

import { useMutation, useQueryClient } from "@tanstack/react-query";
import { getGitpodService } from "../../service/service";
import { useCurrentOrg } from "../organizations/orgs-query";
import { getOrgAuthProvidersQueryKey, OrgAuthProvidersQueryResult } from "./org-auth-providers-query";
import { getOrgAuthProvidersQueryKey } from "./org-auth-providers-query";
import { authProviderClient } from "../../service/public-api";
import { AuthProvider, DeleteAuthProviderRequest } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";

type DeleteAuthProviderArgs = {
providerId: string;
Expand All @@ -22,18 +23,21 @@ export const useDeleteOrgAuthProviderMutation = () => {
throw new Error("No current organization selected");
}

return await getGitpodService().server.deleteOrgAuthProvider({
id: providerId,
organizationId: organization.id,
});
const response = await authProviderClient.deleteAuthProvider(
new DeleteAuthProviderRequest({
authProviderId: providerId,
}),
);

return response;
},
onSuccess: (_, { providerId }) => {
if (!organization) {
throw new Error("No current organization selected");
}

const queryKey = getOrgAuthProvidersQueryKey(organization.id);
queryClient.setQueryData<OrgAuthProvidersQueryResult>(queryKey, (providers) => {
queryClient.setQueryData<AuthProvider[]>(queryKey, (providers) => {
return providers?.filter((p) => p.id !== providerId);
});

Expand Down
Loading

0 comments on commit 21bb3c1

Please sign in to comment.