mirror of
https://github.com/makeplane/plane.git
synced 2026-07-14 14:31:37 +02:00
Merge branch 'develop' of github.com:makeplane/plane-ee into preview
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
TControllerInputFormField,
|
||||
CopyField,
|
||||
TCopyField,
|
||||
CodeBlock,
|
||||
} from "@/components/common";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
@@ -73,7 +74,12 @@ export const InstanceOIDCConfigForm: FC<Props> = (props) => {
|
||||
key: "OIDC_AUTHORIZE_URL",
|
||||
type: "text",
|
||||
label: "Authorize URL",
|
||||
description: "The URL that brings up your IdP's authentication screen when your users click `Sign in with <name of IdP>`",
|
||||
description: (
|
||||
<>
|
||||
The URL that brings up your IdP{"'"}s authentication screen when your users click the{" "}
|
||||
<CodeBlock>{"Continue with"}</CodeBlock>
|
||||
</>
|
||||
),
|
||||
placeholder: "https://example.com/",
|
||||
error: Boolean(errors.OIDC_AUTHORIZE_URL),
|
||||
required: true,
|
||||
@@ -109,7 +115,11 @@ export const InstanceOIDCConfigForm: FC<Props> = (props) => {
|
||||
key: "OIDC_PROVIDER_NAME",
|
||||
type: "text",
|
||||
label: "IdP's name",
|
||||
description: "Optional field for the name that your users see on the `Sign in with` button",
|
||||
description: (
|
||||
<>
|
||||
Optional field for the name that your users see on the <CodeBlock>Continue with</CodeBlock> button
|
||||
</>
|
||||
),
|
||||
placeholder: "Okta",
|
||||
error: Boolean(errors.OIDC_PROVIDER_NAME),
|
||||
required: false,
|
||||
@@ -121,20 +131,32 @@ export const InstanceOIDCConfigForm: FC<Props> = (props) => {
|
||||
key: "Origin_URI",
|
||||
label: "Origin URI",
|
||||
url: `${originURL}/auth/oidc/`,
|
||||
description: "We will generate this for this Plane app. Add this as a trusted origin on your IdP's corresponding field.",
|
||||
description:
|
||||
"We will generate this for this Plane app. Add this as a trusted origin on your IdP's corresponding field.",
|
||||
},
|
||||
{
|
||||
key: "Callback_URI",
|
||||
label: "Callback URI",
|
||||
url: `${originURL}/auth/oidc/callback/`,
|
||||
description:
|
||||
"We will generate this for you. Add this in the `Sign-in redirect URI` field of your IdP.",
|
||||
description: (
|
||||
<>
|
||||
We will generate this for you.Add this in the{" "}
|
||||
<CodeBlock darkerShade>Sign-in redirect URI</CodeBlock> field of
|
||||
your IdP.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "Logout_URI",
|
||||
label: "Logout URI",
|
||||
url: `${originURL}/auth/oidc/logout/`,
|
||||
description: "We will generate this for you. Add this in the `Logout redirect URI` field of your IdP.",
|
||||
description: (
|
||||
<>
|
||||
We will generate this for you. Add this in the{" "}
|
||||
<CodeBlock darkerShade>Logout redirect URI</CodeBlock> field of
|
||||
your IdP.
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -177,8 +199,8 @@ export const InstanceOIDCConfigForm: FC<Props> = (props) => {
|
||||
/>
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
|
||||
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1">
|
||||
<div className="pt-2 text-xl font-medium">IdP-provided details for Plane</div>
|
||||
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1 pt-1">
|
||||
<div className="pt-2.5 text-xl font-medium">IdP-provided details for Plane</div>
|
||||
{OIDC_FORM_FIELDS.map((field) => (
|
||||
<ControllerInput
|
||||
key={field.key}
|
||||
@@ -208,7 +230,7 @@ export const InstanceOIDCConfigForm: FC<Props> = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2 md:col-span-1">
|
||||
<div className="flex flex-col gap-y-4 px-6 py-4 my-2 bg-custom-background-80/60 rounded-lg">
|
||||
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-custom-background-80/60 rounded-lg">
|
||||
<div className="pt-2 text-xl font-medium">Plane-provided details for your IdP</div>
|
||||
{OIDC_SERVICE_DETAILS.map((field) => (
|
||||
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
|
||||
|
||||
@@ -13,7 +13,7 @@ import { PageHeader } from "@/components/common";
|
||||
import { AuthenticationMethodCard } from "@/components/authentication";
|
||||
import { InstanceOIDCConfigForm } from "./form";
|
||||
// icons
|
||||
import OIDCLogo from "/public/logos/oidc-logo.png";
|
||||
import OIDCLogo from "/public/logos/oidc-logo.svg";
|
||||
|
||||
const InstanceOIDCAuthenticationPage = observer(() => {
|
||||
// store
|
||||
@@ -80,7 +80,7 @@ const InstanceOIDCAuthenticationPage = observer(() => {
|
||||
withBorder={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md p-4">
|
||||
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
|
||||
{formattedConfig ? (
|
||||
<InstanceOIDCConfigForm config={formattedConfig} />
|
||||
) : (
|
||||
|
||||
@@ -12,11 +12,13 @@ import {
|
||||
TControllerInputFormField,
|
||||
CopyField,
|
||||
TCopyField,
|
||||
CodeBlock,
|
||||
} from "@/components/common";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useInstance } from "@/hooks/store";
|
||||
import { SAMLAttributeMappingTable } from "@/plane-admin/components/authentication";
|
||||
|
||||
type Props = {
|
||||
config: IFormattedInstanceConfiguration;
|
||||
@@ -62,7 +64,12 @@ export const InstanceSAMLConfigForm: FC<Props> = (props) => {
|
||||
key: "SAML_SSO_URL",
|
||||
type: "text",
|
||||
label: "SSO URL",
|
||||
description: "The URL that brings up your IdP's authentication screen when your users click `Sign in with <name of IdP>`",
|
||||
description: (
|
||||
<>
|
||||
The URL that brings up your IdP{"'"}s authentication screen when your users click the{" "}
|
||||
<CodeBlock>{"Continue with"}</CodeBlock> button
|
||||
</>
|
||||
),
|
||||
placeholder: "https://example.com/sso",
|
||||
error: Boolean(errors.SAML_SSO_URL),
|
||||
required: true,
|
||||
@@ -80,7 +87,11 @@ export const InstanceSAMLConfigForm: FC<Props> = (props) => {
|
||||
key: "SAML_PROVIDER_NAME",
|
||||
type: "text",
|
||||
label: "IdP's name",
|
||||
description: "Optional field for the name that your users see on the `Sign in with` button",
|
||||
description: (
|
||||
<>
|
||||
Optional field for the name that your users see on the <CodeBlock>Continue with</CodeBlock> button
|
||||
</>
|
||||
),
|
||||
placeholder: "Okta",
|
||||
error: Boolean(errors.SAML_PROVIDER_NAME),
|
||||
required: false,
|
||||
@@ -99,14 +110,30 @@ export const InstanceSAMLConfigForm: FC<Props> = (props) => {
|
||||
key: "Callback_URI",
|
||||
label: "Callback URI",
|
||||
url: `${originURL}/auth/saml/callback/`,
|
||||
description:
|
||||
"We will generate this `http-post request` URL that you should paste into your `ACS URL` or `Sign-in call back URL` field on your IdP.",
|
||||
description: (
|
||||
<>
|
||||
We will generate this{" "}
|
||||
<CodeBlock darkerShade>http-post request</CodeBlock> URL that you
|
||||
should paste into your <CodeBlock darkerShade>ACS URL</CodeBlock>{" "}
|
||||
or <CodeBlock darkerShade>Sign-in call back URL</CodeBlock> field
|
||||
on your IdP.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "Logout_URI",
|
||||
label: "Logout URI",
|
||||
url: `${originURL}/auth/saml/logout/`,
|
||||
description: "We will generate this `http-redirect request` URL that you should paste into your `SLS URL` or `Logout URL` field on your IdP.",
|
||||
description: (
|
||||
<>
|
||||
We will generate this{" "}
|
||||
<CodeBlock darkerShade>http-redirect request</CodeBlock> URL that
|
||||
you should paste into your{" "}
|
||||
<CodeBlock darkerShade>SLS URL</CodeBlock> or{" "}
|
||||
<CodeBlock darkerShade>Logout URL</CodeBlock>
|
||||
field on your IdP.
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -147,8 +174,8 @@ export const InstanceSAMLConfigForm: FC<Props> = (props) => {
|
||||
/>
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
|
||||
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1">
|
||||
<div className="pt-2 text-xl font-medium">IdP-provided details for Plane</div>
|
||||
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1 pt-1">
|
||||
<div className="pt-2.5 text-xl font-medium">IdP-provided details for Plane</div>
|
||||
{SAML_FORM_FIELDS.map((field) => (
|
||||
<ControllerInput
|
||||
key={field.key}
|
||||
@@ -180,7 +207,7 @@ export const InstanceSAMLConfigForm: FC<Props> = (props) => {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<p className="text-xs text-custom-text-400">
|
||||
<p className="pt-0.5 text-xs text-custom-text-300">
|
||||
IdP-generated certificate for signing this Plane app as an authorized service provider for your IdP
|
||||
</p>
|
||||
</div>
|
||||
@@ -200,22 +227,14 @@ export const InstanceSAMLConfigForm: FC<Props> = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2 md:col-span-1">
|
||||
<div className="flex flex-col gap-y-4 px-6 py-4 my-2 bg-custom-background-80/60 rounded-lg">
|
||||
<div className="pt-2 text-xl font-medium">Service provider details</div>
|
||||
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-custom-background-80/60 rounded-lg">
|
||||
<div className="pt-2 text-xl font-medium">Plane-provided details for your IdP</div>
|
||||
{SAML_SERVICE_DETAILS.map((field) => (
|
||||
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
|
||||
))}
|
||||
<div className="flex flex-col gap-1">
|
||||
<h4 className="text-sm text-custom-text-200 font-medium">Name ID format</h4>
|
||||
<p className="text-sm text-custom-text-100">emailAddress</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<h4 className="text-sm text-custom-text-200 font-medium">Attribute mapping</h4>
|
||||
<ul className="text-sm text-custom-text-100 list-disc pl-6">
|
||||
<li>first_name to user.firstName</li>
|
||||
<li>last_name to user.lastName</li>
|
||||
<li>email to user.email</li>
|
||||
</ul>
|
||||
<h4 className="text-sm text-custom-text-200 font-medium">Mapping</h4>
|
||||
<SAMLAttributeMappingTable />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -64,7 +64,7 @@ const InstanceSAMLAuthenticationPage = observer(() => {
|
||||
name="SAML"
|
||||
description="Authenticate your users via Security Assertion Markup Language
|
||||
protocol."
|
||||
icon={<Image src={SAMLLogo} height={26} width={26} alt="SAML Logo" className="pb-1 pl-0.5" />}
|
||||
icon={<Image src={SAMLLogo} height={24} width={24} alt="SAML Logo" className="pl-0.5" />}
|
||||
config={
|
||||
<ToggleSwitch
|
||||
value={Boolean(parseInt(enableSAMLConfig))}
|
||||
@@ -81,7 +81,7 @@ const InstanceSAMLAuthenticationPage = observer(() => {
|
||||
withBorder={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md p-4">
|
||||
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
|
||||
{formattedConfig ? (
|
||||
<InstanceSAMLConfigForm config={formattedConfig} />
|
||||
) : (
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// base components
|
||||
import { getAuthenticationModes as getBaseAuthenticationModes } from "ce/components/authentication";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
import Image from "next/image";
|
||||
import { useTheme } from "next-themes";
|
||||
@@ -12,9 +9,12 @@ import {
|
||||
} from "@plane/types";
|
||||
// components
|
||||
import { AuthenticationMethodCard } from "@/components/authentication";
|
||||
// helpers
|
||||
import { getBaseAuthenticationModes } from "@/helpers/authentication.helper";
|
||||
// plane admin components
|
||||
import { OIDCConfiguration, SAMLConfiguration } from "@/plane-admin/components/authentication";
|
||||
// images
|
||||
import OIDCLogo from "@/public/logos/oidc-logo.png";
|
||||
import OIDCLogo from "@/public/logos/oidc-logo.svg";
|
||||
import SAMLLogo from "@/public/logos/saml-logo.svg";
|
||||
|
||||
type TInstanceAuthenticationMethodKeys = TBaseAuthenticationMethods | TInstanceEnterpriseAuthenticationMethodKeys;
|
||||
@@ -41,14 +41,14 @@ export const getAuthenticationModes: (props: TGetAuthenticationModeProps) => TIn
|
||||
key: "oidc",
|
||||
name: "OIDC",
|
||||
description: "Authenticate your users via the OpenID Connect protocol.",
|
||||
icon: <Image src={OIDCLogo} height={20} width={20} alt="OIDC Logo" />,
|
||||
icon: <Image src={OIDCLogo} height={22} width={22} alt="OIDC Logo" />,
|
||||
config: <OIDCConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
||||
},
|
||||
{
|
||||
key: "saml",
|
||||
name: "SAML",
|
||||
description: "Authenticate your users via the Security Assertion Markup Language protocol.",
|
||||
icon: <Image src={SAMLLogo} height={24} width={24} alt="SAML Logo" className="pb-0.5 pl-0.5" />,
|
||||
icon: <Image src={SAMLLogo} height={22} width={22} alt="SAML Logo" className="pl-0.5" />,
|
||||
config: <SAMLConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
||||
},
|
||||
];
|
||||
@@ -68,6 +68,7 @@ export const AuthenticationModes: React.FC<TAuthenticationModeProps> = observer(
|
||||
icon={method.icon}
|
||||
config={method.config}
|
||||
disabled={disabled}
|
||||
unavailable={method.unavailable}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./authentication-modes";
|
||||
export * from "./oidc-config";
|
||||
export * from "./saml-config";
|
||||
export * from "./saml-attribute-mapping-table";
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
export const SAMLAttributeMappingTable = () => (
|
||||
<table className="table-auto border-collapse text-custom-text-200 text-sm">
|
||||
<thead>
|
||||
<tr className="text-left">
|
||||
<th className="border-b border-r border-custom-border-300 px-4 py-1.5">IdP</th>
|
||||
<th className="border-b border-custom-border-300 px-4 py-1.5">Plane</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="border-t border-r border-custom-border-300 px-4 py-1.5">Name ID format</td>
|
||||
<td className="border-t border-custom-border-300 px-4 py-1.5">emailAddress</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border-t border-r border-custom-border-300 px-4 py-1.5">first_name</td>
|
||||
<td className="border-t border-custom-border-300 px-4 py-1.5">user.firstName</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border-t border-r border-custom-border-300 px-4 py-1.5">last_name</td>
|
||||
<td className="border-t border-custom-border-300 px-4 py-1.5">user.lastName</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border-t border-r border-custom-border-300 px-4 py-1.5">email</td>
|
||||
<td className="border-t border-custom-border-300 px-4 py-1.5">user.email</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
@@ -1,5 +1,6 @@
|
||||
# Python imports
|
||||
import requests
|
||||
import os
|
||||
|
||||
# Django imports
|
||||
from django.utils import timezone
|
||||
@@ -57,7 +58,10 @@ class OauthAdapter(Adapter):
|
||||
try:
|
||||
headers = headers or {}
|
||||
response = requests.post(
|
||||
self.get_token_url(), data=data, headers=headers
|
||||
self.get_token_url(),
|
||||
data=data,
|
||||
headers=headers,
|
||||
verify=os.environ.get("SSL_VERIFY", "1") == "1",
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
@@ -73,7 +77,11 @@ class OauthAdapter(Adapter):
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.token_data.get('access_token')}"
|
||||
}
|
||||
response = requests.get(self.get_user_info_url(), headers=headers)
|
||||
response = requests.get(
|
||||
self.get_user_info_url(),
|
||||
headers=headers,
|
||||
verify=os.environ.get("SSL_VERIFY", "1") == "1",
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.RequestException:
|
||||
|
||||
@@ -151,9 +151,7 @@ class SAMLAdapter(Adapter):
|
||||
if errors:
|
||||
if not self.auth.is_authenticated():
|
||||
# Log the errors
|
||||
log_exception(
|
||||
AuthenticationException("Response not authenticated")
|
||||
)
|
||||
log_exception(Exception(errors))
|
||||
raise AuthenticationException(
|
||||
error_code=AUTHENTICATION_ERROR_CODES[
|
||||
"SAML_PROVIDER_ERROR"
|
||||
@@ -161,7 +159,7 @@ class SAMLAdapter(Adapter):
|
||||
error_message="SAML_PROVIDER_ERROR",
|
||||
)
|
||||
# Log the errors
|
||||
log_exception(AuthenticationException(errors))
|
||||
log_exception(Exception(errors))
|
||||
raise AuthenticationException(
|
||||
error_message=AUTHENTICATION_ERROR_CODES[
|
||||
"SAML_PROVIDER_ERROR"
|
||||
|
||||
@@ -64,3 +64,6 @@ FILE_SIZE_LIMIT=5242880
|
||||
|
||||
# Gunicorn Workers
|
||||
GUNICORN_WORKERS=2
|
||||
|
||||
# Add SSL verification
|
||||
SSL_VERIFY=1
|
||||
Reference in New Issue
Block a user