Skip to content

Commit

Permalink
Merge pull request #37 from torbenraab/preview
Browse files Browse the repository at this point in the history
v0.22-dev Update & Fix OIDC
  • Loading branch information
torbenraab committed Sep 11, 2024
2 parents 0a3c8b7 + 3d79f1d commit adb4d63
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 54 deletions.
84 changes: 58 additions & 26 deletions admin/app/authentication/oidc/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import isEmpty from "lodash/isEmpty";
import Link from "next/link";
import { useForm } from "react-hook-form";
// types
import { IFormattedInstanceConfiguration, TInstanceOpenIDConnectAuthenticationConfigurationKeys } from "@plane/types";
import { IFormattedInstanceConfiguration, TInstanceConfigurationKeys, TInstanceOpenIDConnectAuthenticationConfigurationKeys } from "@plane/types";
// ui
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
import { Button, TOAST_TYPE, getButtonStyling, setToast, ToggleSwitch, setPromiseToast } from "@plane/ui";
// components
import {
CodeBlock,
Expand All @@ -30,8 +30,9 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
const { config } = props;
// states
const [isDiscardChangesModalOpen, setIsDiscardChangesModalOpen] = useState(false);
const [isSubmittingAuto, setIsSubmittingAuto] = useState(false);
// store hooks
const { updateInstanceConfigurations } = useInstance();
const { formattedConfig, updateInstanceConfigurations } = useInstance();
// form data
const {
handleSubmit,
Expand All @@ -49,6 +50,37 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
},
});

const updateConfig = async (key: TInstanceConfigurationKeys, value: string) => {
setIsSubmittingAuto(true);

const payload = {
[key]: value,
};

const updateConfigPromise = updateInstanceConfigurations(payload);

setPromiseToast(updateConfigPromise, {
loading: "Saving configuration",
success: {
title: "Success",
message: () => "Configuration saved successfully",
},
error: {
title: "Error",
message: () => "Failed to save configuration",
},
});

await updateConfigPromise
.then(() => {
setIsSubmittingAuto(false);
})
.catch((err) => {
console.error(err);
setIsSubmittingAuto(false);
});
};

const originURL = !isEmpty(API_BASE_URL) ? API_BASE_URL : typeof window !== "undefined" ? window.location.origin : "";

const OIDC_FORM_FIELDS: TControllerInputFormField[] = [
Expand All @@ -57,9 +89,7 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
type: "text",
label: "Authorization Endpoint",
description: (
<>
Example: https://idp.your-company.com/o/authorize/. This is the URL where users will be redirected to
</>
<>Example: https://idp.your-company.com/o/authorize/. This is the URL where users will be redirected to</>
),
placeholder: "https://idp.your-company.com/o/authorize/",
error: Boolean(errors.OIDC_URL_AUTHORIZATION),
Expand All @@ -71,8 +101,8 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
label: "Token Endpoint",
description: (
<>
Example: https://idp.your-company.com/o/token/. This is the URL where we will exchange the code for an
access token.
Example: https://idp.your-company.com/o/token/. This is the URL where we will exchange the code for an access
token.
</>
),
placeholder: "https://idp.your-company.com/o/token/",
Expand All @@ -84,9 +114,7 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
type: "text",
label: "UserInfo Endpoint",
description: (
<>
Example: https://idp.your-company.com/o/userinfo/. This is the URL where we will get user information.
</>
<>Example: https://idp.your-company.com/o/userinfo/. This is the URL where we will get user information.</>
),
placeholder: "https://idp.your-company.com/o/userinfo/",
error: Boolean(errors.OIDC_URL_USERINFO),
Expand All @@ -97,9 +125,7 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
type: "text",
label: "EndSession Endpoint",
description: (
<>
Example: https://idp.your-company.com/o/revoke/. This is the URL where we will revoke the users session.
</>
<>Example: https://idp.your-company.com/o/revoke/. This is the URL where we will revoke the users session.</>
),
placeholder: "https://idp.your-company.com/o/revoke/",
error: Boolean(errors.OIDC_URL_ENDSESSION),
Expand All @@ -109,11 +135,7 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
key: "OIDC_CLIENT_ID",
type: "text",
label: "Client ID",
description: (
<>
Get this from your OpenID Connect Provider.
</>
),
description: <>Get this from your OpenID Connect Provider.</>,
placeholder: "c2ef2e7fc4e9d15aa7630f5637d59e8e4a27ff01dceebdb26b0d267b9adcf3c3",
error: Boolean(errors.OIDC_CLIENT_ID),
required: true,
Expand All @@ -122,11 +144,7 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
key: "OIDC_CLIENT_SECRET",
type: "password",
label: "Client Secret",
description: (
<>
Get this from your OpenID Connect Provider as well.
</>
),
description: <>Get this from your OpenID Connect Provider as well.</>,
placeholder: "gloas-f79cfa9a03c97f6ffab303177a5a6778a53c61e3914ba093412f68a9298a1b28",
error: Boolean(errors.OIDC_CLIENT_SECRET),
required: true,
Expand All @@ -140,8 +158,8 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
url: `${originURL}/auth/oidc/callback/`,
description: (
<>
We will auto-generate this. Paste this into the{" "}
<CodeBlock darkerShade>Redirect URI</CodeBlock> field of your OpenID Connect Provider.
We will auto-generate this. Paste this into the <CodeBlock darkerShade>Redirect URI</CodeBlock> field of your
OpenID Connect Provider.
</>
),
},
Expand Down Expand Up @@ -176,6 +194,9 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
}
};


const automaticOpenIDConnectRedirect = formattedConfig?.IS_OIDC_AUTO ?? "";

return (
<>
<ConfirmDiscardModal
Expand Down Expand Up @@ -221,6 +242,17 @@ export const InstanceOpenIDConnectConfigForm: FC<Props> = (props) => {
{OIDC_SERVICE_FIELD.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
<ToggleSwitch
label="Automatic OpenID Connect Redirect (only activate if tested!)"
value={Boolean(parseInt(automaticOpenIDConnectRedirect))}
onChange={() => {
Boolean(parseInt(automaticOpenIDConnectRedirect)) === true
? updateConfig("IS_OIDC_AUTO", "0")
: updateConfig("IS_OIDC_AUTO", "1");
}}
size="sm"
disabled={isSubmittingAuto}
/>
</div>
</div>
</div>
Expand Down
12 changes: 6 additions & 6 deletions admin/app/authentication/oidc/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { PageHeader } from "@/components/common";
// hooks
import { useInstance } from "@/hooks/store";
// icons
import OpenIDConnectLogo from "@/public/logos/gitlab-logo.svg";
import OpenIDConnectLogo from "@/public/logos/oidc-logo.svg";
// local components
import { InstanceOpenIDConnectConfigForm } from "./form";

Expand All @@ -38,7 +38,7 @@ const InstanceOpenIDConnectAuthenticationPage = observer(() => {
loading: "Saving Configuration...",
success: {
title: "Configuration saved",
message: () => `GitLab authentication is now ${value ? "active" : "disabled"}.`,
message: () => `OIDC authentication is now ${value ? "active" : "disabled"}.`,
},
error: {
title: "Error",
Expand All @@ -57,13 +57,13 @@ const InstanceOpenIDConnectAuthenticationPage = observer(() => {
};
return (
<>
<PageHeader title="GitLab Authentication - Plane Web" />
<PageHeader title="OIDC Authentication - Plane Web" />
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<AuthenticationMethodCard
name="GitLab"
description="Allow members to login or sign up to plane with their GitLab accounts."
icon={<Image src={OpenIDConnectLogo} height={24} width={24} alt="GitLab Logo" />}
name="OIDC"
description="Allow members to login or sign up to plane with their OIDC accounts."
icon={<Image src={OpenIDConnectLogo} height={24} width={24} alt="OIDC Logo" />}
config={
<ToggleSwitch
value={Boolean(parseInt(enableOpenIDConnectConfig))}
Expand Down
29 changes: 8 additions & 21 deletions admin/core/components/authentication/oidc-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,13 @@ export const OpenIDConnectConfiguration: React.FC<Props> = observer((props) => {
const { formattedConfig } = useInstance();
// derived values
const enableOpenIDConnectConfig = formattedConfig?.IS_OIDC_ENABLED ?? "";
const automaticOpenIDConnectRedirect = formattedConfig?.IS_OIDC_AUTO ?? "";
const isOpenIDConnectConfigured = !!formattedConfig?.OIDC_CLIENT_ID && !!formattedConfig?.OIDC_CLIENT_SECRET;

return (
<>
{isOpenIDConnectConfigured ? (
<div className="flex items-center gap-4">
<Link href="/authentication/gitlab" className={cn(getButtonStyling("link-primary", "md"), "font-medium")}>
<Link href="/authentication/oidc" className={cn(getButtonStyling("link-primary", "md"), "font-medium")}>
Edit
</Link>
<ToggleSwitch
Expand All @@ -47,25 +46,13 @@ export const OpenIDConnectConfiguration: React.FC<Props> = observer((props) => {
/>
</div>
) : (
<>
<ToggleSwitch
label="Automatic OpenID Connect Redirect (only activate if tested!)"
value={Boolean(parseInt(automaticOpenIDConnectRedirect))}
onChange={() => {
Boolean(parseInt(automaticOpenIDConnectRedirect)) === true
? updateConfig("IS_OIDC_AUTO", "0")
: updateConfig("IS_OIDC_AUTO", "1");
}}
size="sm"
/>
<Link
href="/authentication/oidc"
className={cn(getButtonStyling("neutral-primary", "sm"), "text-custom-text-300")}
>
<Settings2 className="h-4 w-4 p-0.5 text-custom-text-300/80" />
Configure
</Link>
</>
<Link
href="/authentication/oidc"
className={cn(getButtonStyling("neutral-primary", "sm"), "text-custom-text-300")}
>
<Settings2 className="h-4 w-4 p-0.5 text-custom-text-300/80" />
Configure
</Link>
)}
</>
);
Expand Down
2 changes: 1 addition & 1 deletion web/core/components/account/oauth/oauth-options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const OAuthOptions: React.FC<TOAuthOptionProps> = observer(() => {
// hooks
const { config } = useInstance();

const isOAuthEnabled = (config && (config?.is_google_enabled || config?.is_github_enabled || config?.is_gitlab_enabled)) || false;
const isOAuthEnabled = (config && (config?.is_google_enabled || config?.is_github_enabled || config?.is_gitlab_enabled || config?.is_oidc_enabled)) || false;

if (!isOAuthEnabled) return null;

Expand Down

0 comments on commit adb4d63

Please sign in to comment.