mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 12:28:54 +00:00
Merge pull request #2667 from scott-ray-wilson/oidc-default-org-slug
Feature: OIDC Default Org
This commit is contained in:
@@ -29,6 +29,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
}).extend({
|
}).extend({
|
||||||
isMigrationModeOn: z.boolean(),
|
isMigrationModeOn: z.boolean(),
|
||||||
defaultAuthOrgSlug: z.string().nullable(),
|
defaultAuthOrgSlug: z.string().nullable(),
|
||||||
|
defaultAuthOrgAuthEnforced: z.boolean().nullish(),
|
||||||
|
defaultAuthOrgAuthMethod: z.string().nullish(),
|
||||||
isSecretScanningDisabled: z.boolean()
|
isSecretScanningDisabled: z.boolean()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ export const orgServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (authEnforced !== undefined) {
|
if (authEnforced !== undefined) {
|
||||||
if (!plan?.samlSSO || !plan.oidcSSO)
|
if (!plan?.samlSSO && !plan.oidcSSO)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Failed to enforce/un-enforce SSO due to plan restriction. Upgrade plan to enforce/un-enforce SSO."
|
message: "Failed to enforce/un-enforce SSO due to plan restriction. Upgrade plan to enforce/un-enforce SSO."
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,9 +14,31 @@ export const superAdminDALFactory = (db: TDbClient) => {
|
|||||||
const config = await (tx || db)(TableName.SuperAdmin)
|
const config = await (tx || db)(TableName.SuperAdmin)
|
||||||
.where(`${TableName.SuperAdmin}.id`, id)
|
.where(`${TableName.SuperAdmin}.id`, id)
|
||||||
.leftJoin(TableName.Organization, `${TableName.SuperAdmin}.defaultAuthOrgId`, `${TableName.Organization}.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(
|
.select(
|
||||||
db.ref("*").withSchema(TableName.SuperAdmin) as unknown as keyof TSuperAdmin,
|
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();
|
.first();
|
||||||
|
|
||||||
@@ -27,7 +49,11 @@ export const superAdminDALFactory = (db: TDbClient) => {
|
|||||||
return {
|
return {
|
||||||
...config,
|
...config,
|
||||||
defaultAuthOrgSlug: config?.defaultAuthOrgSlug || null
|
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) => {
|
const updateById = async (id: string, data: TSuperAdminUpdate, tx?: Knex) => {
|
||||||
|
|||||||
@@ -29,7 +29,13 @@ type TSuperAdminServiceFactoryDep = {
|
|||||||
export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFactory>;
|
export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFactory>;
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// 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 = "infisical-admin-cfg";
|
||||||
const ADMIN_CONFIG_KEY_EXP = 60; // 60s
|
const ADMIN_CONFIG_KEY_EXP = 60; // 60s
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export type TServerConfig = {
|
|||||||
isSecretScanningDisabled: boolean;
|
isSecretScanningDisabled: boolean;
|
||||||
defaultAuthOrgSlug: string | null;
|
defaultAuthOrgSlug: string | null;
|
||||||
defaultAuthOrgId: string | null;
|
defaultAuthOrgId: string | null;
|
||||||
|
defaultAuthOrgAuthMethod?: string | null;
|
||||||
|
defaultAuthOrgAuthEnforced?: boolean | null;
|
||||||
enabledLoginMethods: LoginMethod[];
|
enabledLoginMethods: LoginMethod[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ export enum AuthMethod {
|
|||||||
JUMPCLOUD_SAML = "jumpcloud-saml",
|
JUMPCLOUD_SAML = "jumpcloud-saml",
|
||||||
KEYCLOAK_SAML = "keycloak-saml",
|
KEYCLOAK_SAML = "keycloak-saml",
|
||||||
LDAP = "ldap",
|
LDAP = "ldap",
|
||||||
OIDC = "oidc"
|
OIDC = "oidc",
|
||||||
|
SAML = "saml"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
|
|||||||
@@ -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 { useTranslation } from "react-i18next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
@@ -17,6 +17,7 @@ import { Button, IconButton, Input, Tooltip } from "@app/components/v2";
|
|||||||
import { useServerConfig } from "@app/context";
|
import { useServerConfig } from "@app/context";
|
||||||
import { useFetchServerStatus } from "@app/hooks/api";
|
import { useFetchServerStatus } from "@app/hooks/api";
|
||||||
import { LoginMethod } from "@app/hooks/api/admin/types";
|
import { LoginMethod } from "@app/hooks/api/admin/types";
|
||||||
|
import { AuthMethod } from "@app/hooks/api/users/types";
|
||||||
|
|
||||||
import { useNavigateToSelectOrganization } from "../../Login.utils";
|
import { useNavigateToSelectOrganization } from "../../Login.utils";
|
||||||
|
|
||||||
@@ -51,17 +52,33 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
|||||||
router.push(redirectUrl);
|
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(() => {
|
useEffect(() => {
|
||||||
if (serverDetails?.samlDefaultOrgSlug) redirectToSaml(serverDetails.samlDefaultOrgSlug);
|
if (serverDetails?.samlDefaultOrgSlug) redirectToSaml(serverDetails.samlDefaultOrgSlug);
|
||||||
}, [serverDetails?.samlDefaultOrgSlug]);
|
}, [serverDetails?.samlDefaultOrgSlug]);
|
||||||
|
|
||||||
const handleSaml = useCallback((step: number) => {
|
const handleSaml = () => {
|
||||||
if (config.defaultAuthOrgSlug) {
|
if (config.defaultAuthOrgSlug) {
|
||||||
redirectToSaml(config.defaultAuthOrgSlug);
|
redirectToSaml(config.defaultAuthOrgSlug);
|
||||||
} else {
|
} else {
|
||||||
setStep(step);
|
setStep(2);
|
||||||
}
|
}
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
|
const handleOidc = () => {
|
||||||
|
if (config.defaultAuthOrgSlug) {
|
||||||
|
redirectToOidc(config.defaultAuthOrgSlug);
|
||||||
|
} else {
|
||||||
|
setStep(3);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const shouldDisplayLoginMethod = (method: LoginMethod) =>
|
const shouldDisplayLoginMethod = (method: LoginMethod) =>
|
||||||
!config.enabledLoginMethods || config.enabledLoginMethods.includes(method);
|
!config.enabledLoginMethods || config.enabledLoginMethods.includes(method);
|
||||||
@@ -142,6 +159,46 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
|||||||
setIsLoading(false);
|
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 (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={handleLogin}
|
onSubmit={handleLogin}
|
||||||
@@ -156,9 +213,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
|||||||
<Button
|
<Button
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={handleSaml}
|
||||||
handleSaml(2);
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
||||||
className="mx-0 h-10 w-full"
|
className="mx-0 h-10 w-full"
|
||||||
>
|
>
|
||||||
@@ -171,9 +226,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
|||||||
<Button
|
<Button
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={handleOidc}
|
||||||
setStep(3);
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
||||||
className="mx-0 h-10 w-full"
|
className="mx-0 h-10 w-full"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -212,8 +212,9 @@ export const AdminDashboardPage = () => {
|
|||||||
Default organization
|
Default organization
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
<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
|
Select the default organization you want to set for SAML/LDAP/OIDC based logins.
|
||||||
selected, user logins will be automatically scoped to the selected organization.
|
When selected, user logins will be automatically scoped to the selected
|
||||||
|
organization.
|
||||||
</div>
|
</div>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|||||||
Reference in New Issue
Block a user