Skip to content
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

feat(ui): add gitlab sso support #2219

Merged
merged 6 commits into from
May 22, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,13 @@ export const PROVIDER_METAS: Array<{
domain: 'google.com',
displayName: 'Google'
}
},
{
name: 'gitlab',
enum: OAuthProvider.Gitlab,
meta: {
domain: 'gitlab.com',
displayName: 'GitLab'
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ import {
FormLabel,
FormMessage
} from '@/components/ui/form'
import { IconSpinner } from '@/components/ui/icons'
import {
IconGitHub,
IconGitLab,
IconGoogle,
IconSpinner
} from '@/components/ui/icons'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
Expand Down Expand Up @@ -157,6 +162,12 @@ export default function OAuthCredentialForm({
variables: { provider: providerValue }
})

const accessTokenPlaceholder = React.useMemo(() => {
if (!isNew) return new Array(36).fill('*').join('')

return 'e.g. e363c08d7e9ca4e66e723a53f38a21f6a54c1b83'
}, [isNew])

return (
<Form {...form}>
<div className={cn('grid gap-2', className)} {...props}>
Expand All @@ -181,7 +192,7 @@ export default function OAuthCredentialForm({
<FormLabel>Provider</FormLabel>
<FormControl>
<RadioGroup
className="flex gap-6"
className="flex gap-8"
orientation="horizontal"
onValueChange={onChange}
{...rest}
Expand All @@ -192,7 +203,11 @@ export default function OAuthCredentialForm({
id="r_github"
disabled={!isNew}
/>
<Label className="cursor-pointer" htmlFor="r_github">
<Label
className="flex cursor-pointer items-center gap-2"
htmlFor="r_github"
>
<IconGitHub className="h-5 w-5" />
GitHub
</Label>
</div>
Expand All @@ -202,10 +217,28 @@ export default function OAuthCredentialForm({
id="r_google"
disabled={!isNew}
/>
<Label className="cursor-pointer" htmlFor="r_google">
<Label
className="flex cursor-pointer items-center gap-2"
htmlFor="r_google"
>
<IconGoogle className="h-5 w-5" />
Google
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem
value={OAuthProvider.Gitlab}
id="r_gitlab"
disabled={!isNew}
/>
<Label
className="flex cursor-pointer items-center gap-2"
htmlFor="r_gitlab"
>
<IconGitLab className="h-5 w-5" />
GitLab
</Label>
</div>
</RadioGroup>
</FormControl>
<FormMessage />
Expand Down Expand Up @@ -265,19 +298,19 @@ export default function OAuthCredentialForm({
name="clientSecret"
render={({ field }) => (
<FormItem>
<FormLabel required>Client Secret</FormLabel>
<FormLabel required={isNew}>Client Secret</FormLabel>
<FormControl>
<Input
{...field}
placeholder={
isNew
? 'e.g. e363c08d7e9ca4e66e723a53f38a21f6a54c1b83'
: '*****'
}
className={cn({
'placeholder:translate-y-[10%] !placeholder-foreground':
!isNew
})}
placeholder={accessTokenPlaceholder}
autoCapitalize="none"
autoComplete="off"
autoCorrect="off"
type="password"
{...field}
/>
</FormControl>
<FormMessage />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import LoadingWrapper from '@/components/loading-wrapper'

import { PROVIDER_METAS } from './constant'
import { SSOHeader } from './sso-header'
import { IconGitHub, IconGitLab, IconGoogle } from '@/components/ui/icons'

export const oauthCredential = graphql(/* GraphQL */ `
query OAuthCredential($provider: OAuthProvider!) {
Expand All @@ -41,11 +42,19 @@ const OAuthCredentialList = () => {
query: oauthCredential,
variables: { provider: OAuthProvider.Google }
})
const [{ data: gitlabData, fetching: fetchingGitlab }] = useQuery({
query: oauthCredential,
variables: { provider: OAuthProvider.Gitlab }
})

const isLoading = fetchingGithub || fetchingGoogle
const isLoading = fetchingGithub || fetchingGoogle || fetchingGitlab
const credentialList = React.useMemo(() => {
return compact([githubData?.oauthCredential, googleData?.oauthCredential])
}, [githubData, googleData])
return compact([
githubData?.oauthCredential,
googleData?.oauthCredential,
gitlabData?.oauthCredential
])
}, [githubData, googleData, gitlabData])

const router = useRouter()
const createButton = (
Expand Down Expand Up @@ -100,7 +109,7 @@ const OAuthCredentialList = () => {
)
})}
</div>
{credentialList.length < 2 && (
{credentialList.length < 3 && (
<div className="mt-4 flex justify-end">{createButton}</div>
)}
</div>
Expand All @@ -115,15 +124,19 @@ const OauthCredentialCard = ({
const meta = React.useMemo(() => {
return find(PROVIDER_METAS, { enum: data?.provider })?.meta
}, [data])

if (!data) return null

return (
<Card>
<CardHeader className="border-b p-4">
<div className="flex items-center justify-between">
<CardTitle className="text-xl">
{meta?.displayName || data?.provider}
<CardTitle className="flex items-center gap-2 text-xl">
<OAuthProviderIcon provider={data.provider} />
{meta?.displayName || data.provider}
</CardTitle>
<Link
href={`/settings/sso/detail/${data?.provider.toLowerCase()}`}
href={`/settings/sso/detail/${data.provider.toLowerCase()}`}
className={buttonVariants({ variant: 'secondary' })}
>
View
Expand All @@ -144,4 +157,17 @@ const OauthCredentialCard = ({
)
}

export { OAuthCredentialList as OauthCredentialList }
function OAuthProviderIcon({ provider }: { provider: OAuthProvider }) {
switch (provider) {
case OAuthProvider.Github:
return <IconGitHub className="h-6 w-6" />
case OAuthProvider.Google:
return <IconGoogle className="h-6 w-6" />
case OAuthProvider.Gitlab:
return <IconGitLab className="h-6 w-6" />
default:
return null
}
}

export { OAuthCredentialList }
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Metadata } from 'next'

import { OauthCredentialList } from './components/oauth-credential-list'
import { OAuthCredentialList } from './components/oauth-credential-list'

export const metadata: Metadata = {
title: 'SSO'
}

export default function IndexPage() {
return <OauthCredentialList />
return <OAuthCredentialList />
}
14 changes: 12 additions & 2 deletions ee/tabby-ui/app/auth/signin/components/signin-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import useRouterStuff from '@/lib/hooks/use-router-stuff'
import { useAllowSelfSignup } from '@/lib/hooks/use-server-info'
import { useSession, useSignIn } from '@/lib/tabby/auth'
import fetcher from '@/lib/tabby/fetcher'
import { IconGithub, IconGoogle, IconSpinner } from '@/components/ui/icons'
import {
IconGitLab,
IconGithub,
IconGoogle,
IconSpinner
} from '@/components/ui/icons'

import UserSignInForm from './user-signin-form'

Expand Down Expand Up @@ -78,7 +83,7 @@ export default function SigninSection() {
<div className="grow border-t "></div>
</div>
)}
<div className="mx-auto flex items-center gap-6">
<div className="mx-auto flex items-center gap-8">
{data?.includes('github') && (
<a href={`/oauth/signin?provider=github`}>
<IconGithub className="h-8 w-8" />
Expand All @@ -89,6 +94,11 @@ export default function SigninSection() {
<IconGoogle className="h-8 w-8" />
</a>
)}
{data?.includes('gitlab') && (
<a href={`/oauth/signin?provider=gitlab`}>
<IconGitLab className="h-8 w-8" />
</a>
)}
</div>
{!!errorMessage && (
<div className="mt-4 text-destructive">{errorMessage}</div>
Expand Down
6 changes: 5 additions & 1 deletion ee/tabby-webserver/src/routes/oauth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@
}

async fn providers_handler(state: State<OAuthState>) -> Json<Vec<OAuthProvider>> {
let candidates = vec![OAuthProvider::Google, OAuthProvider::Github];
let candidates = vec![
wsxiaoys marked this conversation as resolved.
Show resolved Hide resolved
OAuthProvider::Google,
OAuthProvider::Github,
OAuthProvider::Gitlab,
];

Check warning on line 61 in ee/tabby-webserver/src/routes/oauth.rs

View check run for this annotation

Codecov / codecov/patch

ee/tabby-webserver/src/routes/oauth.rs#L57-L61

Added lines #L57 - L61 were not covered by tests
let mut providers = vec![];

for x in candidates {
Expand Down
Loading