Merge pull request #2667 from scott-ray-wilson/oidc-default-org-slug

Feature: OIDC Default Org
This commit is contained in:
Maidul Islam
2024-10-31 21:56:53 -04:00
committed by GitHub
8 changed files with 108 additions and 17 deletions

View File

@@ -29,6 +29,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
}).extend({
isMigrationModeOn: z.boolean(),
defaultAuthOrgSlug: z.string().nullable(),
defaultAuthOrgAuthEnforced: z.boolean().nullish(),
defaultAuthOrgAuthMethod: z.string().nullish(),
isSecretScanningDisabled: z.boolean()
})
})

View File

@@ -291,7 +291,7 @@ export const orgServiceFactory = ({
}
if (authEnforced !== undefined) {
if (!plan?.samlSSO || !plan.oidcSSO)
if (!plan?.samlSSO && !plan.oidcSSO)
throw new BadRequestError({
message: "Failed to enforce/un-enforce SSO due to plan restriction. Upgrade plan to enforce/un-enforce SSO."
});

View File

@@ -14,9 +14,31 @@ export const superAdminDALFactory = (db: TDbClient) => {
const config = await (tx || db)(TableName.SuperAdmin)
.where(`${TableName.SuperAdmin}.id`, id)
.leftJoin(TableName.Organization, `${TableName.SuperAdmin}.defaultAuthOrgId`, `${TableName.Organization}.id`)
.leftJoin(TableName.SamlConfig, (qb) => {
qb.on(`${TableName.SamlConfig}.orgId`, "=", `${TableName.Organization}.id`).andOn(
`${TableName.SamlConfig}.isActive`,
"=",
db.raw("true")
);
})
.leftJoin(TableName.OidcConfig, (qb) => {
qb.on(`${TableName.OidcConfig}.orgId`, "=", `${TableName.Organization}.id`).andOn(
`${TableName.OidcConfig}.isActive`,
"=",
db.raw("true")
);
})
.select(
db.ref("*").withSchema(TableName.SuperAdmin) as unknown as keyof TSuperAdmin,
db.ref("slug").withSchema(TableName.Organization).as("defaultAuthOrgSlug")
db.ref("slug").withSchema(TableName.Organization).as("defaultAuthOrgSlug"),
db.ref("authEnforced").withSchema(TableName.Organization).as("defaultAuthOrgAuthEnforced"),
db.raw(`
CASE
WHEN ${TableName.SamlConfig}."orgId" IS NOT NULL THEN 'saml'
WHEN ${TableName.OidcConfig}."orgId" IS NOT NULL THEN 'oidc'
ELSE NULL
END as "defaultAuthOrgAuthMethod"
`)
)
.first();
@@ -27,7 +49,11 @@ export const superAdminDALFactory = (db: TDbClient) => {
return {
...config,
defaultAuthOrgSlug: config?.defaultAuthOrgSlug || null
} as TSuperAdmin & { defaultAuthOrgSlug: string | null };
} as TSuperAdmin & {
defaultAuthOrgSlug: string | null;
defaultAuthOrgAuthEnforced?: boolean | null;
defaultAuthOrgAuthMethod?: string | null;
};
};
const updateById = async (id: string, data: TSuperAdminUpdate, tx?: Knex) => {

View File

@@ -29,7 +29,13 @@ type TSuperAdminServiceFactoryDep = {
export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFactory>;
// eslint-disable-next-line
export let getServerCfg: () => Promise<TSuperAdmin & { defaultAuthOrgSlug: string | null }>;
export let getServerCfg: () => Promise<
TSuperAdmin & {
defaultAuthOrgSlug: string | null;
defaultAuthOrgAuthEnforced?: boolean | null;
defaultAuthOrgAuthMethod?: string | null;
}
>;
const ADMIN_CONFIG_KEY = "infisical-admin-cfg";
const ADMIN_CONFIG_KEY_EXP = 60; // 60s

View File

@@ -19,6 +19,8 @@ export type TServerConfig = {
isSecretScanningDisabled: boolean;
defaultAuthOrgSlug: string | null;
defaultAuthOrgId: string | null;
defaultAuthOrgAuthMethod?: string | null;
defaultAuthOrgAuthEnforced?: boolean | null;
enabledLoginMethods: LoginMethod[];
};

View File

@@ -11,7 +11,8 @@ export enum AuthMethod {
JUMPCLOUD_SAML = "jumpcloud-saml",
KEYCLOAK_SAML = "keycloak-saml",
LDAP = "ldap",
OIDC = "oidc"
OIDC = "oidc",
SAML = "saml"
}
export type User = {

View File

@@ -1,4 +1,4 @@
import { FormEvent, useCallback, useEffect, useRef, useState } from "react";
import { FormEvent, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import Link from "next/link";
import { useRouter } from "next/router";
@@ -17,6 +17,7 @@ import { Button, IconButton, Input, Tooltip } from "@app/components/v2";
import { useServerConfig } from "@app/context";
import { useFetchServerStatus } from "@app/hooks/api";
import { LoginMethod } from "@app/hooks/api/admin/types";
import { AuthMethod } from "@app/hooks/api/users/types";
import { useNavigateToSelectOrganization } from "../../Login.utils";
@@ -51,17 +52,33 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
router.push(redirectUrl);
};
const redirectToOidc = (orgSlug: string) => {
const callbackPort = queryParams.get("callback_port");
const redirectUrl = `/api/v1/sso/oidc/login?orgSlug=${orgSlug}${
callbackPort ? `&callbackPort=${callbackPort}` : ""
}`;
router.push(redirectUrl);
};
useEffect(() => {
if (serverDetails?.samlDefaultOrgSlug) redirectToSaml(serverDetails.samlDefaultOrgSlug);
}, [serverDetails?.samlDefaultOrgSlug]);
const handleSaml = useCallback((step: number) => {
const handleSaml = () => {
if (config.defaultAuthOrgSlug) {
redirectToSaml(config.defaultAuthOrgSlug);
} else {
setStep(step);
setStep(2);
}
}, []);
};
const handleOidc = () => {
if (config.defaultAuthOrgSlug) {
redirectToOidc(config.defaultAuthOrgSlug);
} else {
setStep(3);
}
};
const shouldDisplayLoginMethod = (method: LoginMethod) =>
!config.enabledLoginMethods || config.enabledLoginMethods.includes(method);
@@ -142,6 +159,46 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
setIsLoading(false);
};
if (config.defaultAuthOrgAuthEnforced && config.defaultAuthOrgAuthMethod) {
return (
<form
onSubmit={handleLogin}
className="mx-auto flex w-full flex-col items-center justify-center"
>
<h1 className="mb-8 bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
Login to Infisical
</h1>
<RegionSelect />
{config.defaultAuthOrgAuthMethod === AuthMethod.SAML && (
<div className="w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
<Button
colorSchema="primary"
variant="outline_bg"
onClick={handleSaml}
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
className="mx-0 h-10 w-full"
>
Continue with SAML
</Button>
</div>
)}
{config.defaultAuthOrgAuthMethod === AuthMethod.OIDC && (
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
<Button
colorSchema="primary"
variant="outline_bg"
onClick={handleOidc}
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
className="mx-0 h-10 w-full"
>
Continue with OIDC
</Button>
</div>
)}
</form>
);
}
return (
<form
onSubmit={handleLogin}
@@ -156,9 +213,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
<Button
colorSchema="primary"
variant="outline_bg"
onClick={() => {
handleSaml(2);
}}
onClick={handleSaml}
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
className="mx-0 h-10 w-full"
>
@@ -171,9 +226,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
<Button
colorSchema="primary"
variant="outline_bg"
onClick={() => {
setStep(3);
}}
onClick={handleOidc}
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
className="mx-0 h-10 w-full"
>

View File

@@ -212,8 +212,9 @@ export const AdminDashboardPage = () => {
Default organization
</div>
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
Select the default organization you want to set for SAML/LDAP based logins. When
selected, user logins will be automatically scoped to the selected organization.
Select the default organization you want to set for SAML/LDAP/OIDC based logins.
When selected, user logins will be automatically scoped to the selected
organization.
</div>
<Controller
control={control}