mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #2026 from Infisical/feat/allow-toggling-login-options-as-admin
feat: allowed toggling login options as admin
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.SuperAdmin, "enabledLoginMethods"))) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (tb) => {
|
||||||
|
tb.specificType("enabledLoginMethods", "text[]");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.SuperAdmin, "enabledLoginMethods")) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||||
|
t.dropColumn("enabledLoginMethods");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,8 @@ export const SuperAdminSchema = z.object({
|
|||||||
trustSamlEmails: z.boolean().default(false).nullable().optional(),
|
trustSamlEmails: z.boolean().default(false).nullable().optional(),
|
||||||
trustLdapEmails: z.boolean().default(false).nullable().optional(),
|
trustLdapEmails: z.boolean().default(false).nullable().optional(),
|
||||||
trustOidcEmails: z.boolean().default(false).nullable().optional(),
|
trustOidcEmails: z.boolean().default(false).nullable().optional(),
|
||||||
defaultAuthOrgId: z.string().uuid().nullable().optional()
|
defaultAuthOrgId: z.string().uuid().nullable().optional(),
|
||||||
|
enabledLoginMethods: z.string().array().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal
|
|||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
|
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||||
@@ -417,6 +418,13 @@ export const ldapConfigServiceFactory = ({
|
|||||||
}: TLdapLoginDTO) => {
|
}: TLdapLoginDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const serverCfg = await getServerCfg();
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.LDAP)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with LDAP is disabled by administrator."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let userAlias = await userAliasDAL.findOne({
|
let userAlias = await userAliasDAL.findOne({
|
||||||
externalId,
|
externalId,
|
||||||
orgId,
|
orgId,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
|
|||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
|
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||||
@@ -157,6 +158,13 @@ export const oidcConfigServiceFactory = ({
|
|||||||
|
|
||||||
const oidcLogin = async ({ externalId, email, firstName, lastName, orgId, callbackPort }: TOidcLoginDTO) => {
|
const oidcLogin = async ({ externalId, email, firstName, lastName, orgId, callbackPort }: TOidcLoginDTO) => {
|
||||||
const serverCfg = await getServerCfg();
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.OIDC)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with OIDC is disabled by administrator."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const userAlias = await userAliasDAL.findOne({
|
const userAlias = await userAliasDAL.findOne({
|
||||||
externalId,
|
externalId,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
|
|||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
|
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||||
@@ -335,6 +336,13 @@ export const samlConfigServiceFactory = ({
|
|||||||
}: TSamlLoginDTO) => {
|
}: TSamlLoginDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const serverCfg = await getServerCfg();
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.SAML)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with SAML is disabled by administrator."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const userAlias = await userAliasDAL.findOne({
|
const userAlias = await userAliasDAL.findOne({
|
||||||
externalId,
|
externalId,
|
||||||
orgId,
|
orgId,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
|
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||||
|
|
||||||
export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||||
@@ -54,7 +55,14 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
trustSamlEmails: z.boolean().optional(),
|
trustSamlEmails: z.boolean().optional(),
|
||||||
trustLdapEmails: z.boolean().optional(),
|
trustLdapEmails: z.boolean().optional(),
|
||||||
trustOidcEmails: z.boolean().optional(),
|
trustOidcEmails: z.boolean().optional(),
|
||||||
defaultAuthOrgId: z.string().optional().nullable()
|
defaultAuthOrgId: z.string().optional().nullable(),
|
||||||
|
enabledLoginMethods: z
|
||||||
|
.nativeEnum(LoginMethod)
|
||||||
|
.array()
|
||||||
|
.optional()
|
||||||
|
.refine((methods) => !methods || methods.length > 0, {
|
||||||
|
message: "At least one login method should be enabled."
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -70,7 +78,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const config = await server.services.superAdmin.updateServerCfg(req.body);
|
const config = await server.services.superAdmin.updateServerCfg(req.body, req.permission.id);
|
||||||
return { config };
|
return { config };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
|||||||
import { TokenType } from "../auth-token/auth-token-types";
|
import { TokenType } from "../auth-token/auth-token-types";
|
||||||
import { TOrgDALFactory } from "../org/org-dal";
|
import { TOrgDALFactory } from "../org/org-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||||
|
import { LoginMethod } from "../super-admin/super-admin-types";
|
||||||
import { TUserDALFactory } from "../user/user-dal";
|
import { TUserDALFactory } from "../user/user-dal";
|
||||||
import { enforceUserLockStatus, validateProviderAuthToken } from "./auth-fns";
|
import { enforceUserLockStatus, validateProviderAuthToken } from "./auth-fns";
|
||||||
import {
|
import {
|
||||||
@@ -158,9 +159,22 @@ export const authLoginServiceFactory = ({
|
|||||||
const userEnc = await userDAL.findUserEncKeyByUsername({
|
const userEnc = await userDAL.findUserEncKeyByUsername({
|
||||||
username: email
|
username: email
|
||||||
});
|
});
|
||||||
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
if (
|
||||||
|
serverCfg.enabledLoginMethods &&
|
||||||
|
!serverCfg.enabledLoginMethods.includes(LoginMethod.EMAIL) &&
|
||||||
|
!providerAuthToken
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with email is disabled by administrator."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!userEnc || (userEnc && !userEnc.isAccepted)) {
|
if (!userEnc || (userEnc && !userEnc.isAccepted)) {
|
||||||
throw new Error("Failed to find user");
|
throw new Error("Failed to find user");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
|
if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
|
||||||
validateProviderAuthToken(providerAuthToken as string, email);
|
validateProviderAuthToken(providerAuthToken as string, email);
|
||||||
}
|
}
|
||||||
@@ -507,6 +521,40 @@ export const authLoginServiceFactory = ({
|
|||||||
let user = await userDAL.findUserByUsername(email);
|
let user = await userDAL.findUserByUsername(email);
|
||||||
const serverCfg = await getServerCfg();
|
const serverCfg = await getServerCfg();
|
||||||
|
|
||||||
|
if (serverCfg.enabledLoginMethods) {
|
||||||
|
switch (authMethod) {
|
||||||
|
case AuthMethod.GITHUB: {
|
||||||
|
if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GITHUB)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with Github is disabled by administrator.",
|
||||||
|
name: "Oauth 2 login"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AuthMethod.GOOGLE: {
|
||||||
|
if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GOOGLE)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with Google is disabled by administrator.",
|
||||||
|
name: "Oauth 2 login"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AuthMethod.GITLAB: {
|
||||||
|
if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GITLAB)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Login with Gitlab is disabled by administrator.",
|
||||||
|
name: "Oauth 2 login"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { AuthMethod } from "../auth/auth-type";
|
|||||||
import { TOrgServiceFactory } from "../org/org-service";
|
import { TOrgServiceFactory } from "../org/org-service";
|
||||||
import { TUserDALFactory } from "../user/user-dal";
|
import { TUserDALFactory } from "../user/user-dal";
|
||||||
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
||||||
import { TAdminSignUpDTO } from "./super-admin-types";
|
import { LoginMethod, TAdminSignUpDTO } from "./super-admin-types";
|
||||||
|
|
||||||
type TSuperAdminServiceFactoryDep = {
|
type TSuperAdminServiceFactoryDep = {
|
||||||
serverCfgDAL: TSuperAdminDALFactory;
|
serverCfgDAL: TSuperAdminDALFactory;
|
||||||
@@ -79,7 +79,37 @@ export const superAdminServiceFactory = ({
|
|||||||
return newCfg;
|
return newCfg;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateServerCfg = async (data: TSuperAdminUpdate) => {
|
const updateServerCfg = async (data: TSuperAdminUpdate, userId: string) => {
|
||||||
|
if (data.enabledLoginMethods) {
|
||||||
|
const superAdminUser = await userDAL.findById(userId);
|
||||||
|
const loginMethodToAuthMethod = {
|
||||||
|
[LoginMethod.EMAIL]: [AuthMethod.EMAIL],
|
||||||
|
[LoginMethod.GOOGLE]: [AuthMethod.GOOGLE],
|
||||||
|
[LoginMethod.GITLAB]: [AuthMethod.GITLAB],
|
||||||
|
[LoginMethod.GITHUB]: [AuthMethod.GITHUB],
|
||||||
|
[LoginMethod.LDAP]: [AuthMethod.LDAP],
|
||||||
|
[LoginMethod.OIDC]: [AuthMethod.OIDC],
|
||||||
|
[LoginMethod.SAML]: [
|
||||||
|
AuthMethod.AZURE_SAML,
|
||||||
|
AuthMethod.GOOGLE_SAML,
|
||||||
|
AuthMethod.JUMPCLOUD_SAML,
|
||||||
|
AuthMethod.KEYCLOAK_SAML,
|
||||||
|
AuthMethod.OKTA_SAML
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
!data.enabledLoginMethods.some((loginMethod) =>
|
||||||
|
loginMethodToAuthMethod[loginMethod as LoginMethod].some(
|
||||||
|
(authMethod) => superAdminUser.authMethods?.includes(authMethod)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "You must configure at least one auth method to prevent account lockout"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
const updatedServerCfg = await serverCfgDAL.updateById(ADMIN_CONFIG_DB_UUID, data);
|
const updatedServerCfg = await serverCfgDAL.updateById(ADMIN_CONFIG_DB_UUID, data);
|
||||||
|
|
||||||
await keyStore.setItemWithExpiry(ADMIN_CONFIG_KEY, ADMIN_CONFIG_KEY_EXP, JSON.stringify(updatedServerCfg));
|
await keyStore.setItemWithExpiry(ADMIN_CONFIG_KEY, ADMIN_CONFIG_KEY_EXP, JSON.stringify(updatedServerCfg));
|
||||||
@@ -167,7 +197,7 @@ export const superAdminServiceFactory = ({
|
|||||||
orgName: initialOrganizationName
|
orgName: initialOrganizationName
|
||||||
});
|
});
|
||||||
|
|
||||||
await updateServerCfg({ initialized: true });
|
await updateServerCfg({ initialized: true }, userInfo.user.id);
|
||||||
const token = await authService.generateUserTokens({
|
const token = await authService.generateUserTokens({
|
||||||
user: userInfo.user,
|
user: userInfo.user,
|
||||||
authMethod: AuthMethod.EMAIL,
|
authMethod: AuthMethod.EMAIL,
|
||||||
|
|||||||
@@ -15,3 +15,13 @@ export type TAdminSignUpDTO = {
|
|||||||
ip: string;
|
ip: string;
|
||||||
userAgent: string;
|
userAgent: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum LoginMethod {
|
||||||
|
EMAIL = "email",
|
||||||
|
GOOGLE = "google",
|
||||||
|
GITHUB = "github",
|
||||||
|
GITLAB = "gitlab",
|
||||||
|
SAML = "saml",
|
||||||
|
LDAP = "ldap",
|
||||||
|
OIDC = "oidc"
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import { faGithub, faGitlab, faGoogle } from "@fortawesome/free-brands-svg-icons
|
|||||||
import { faEnvelope } from "@fortawesome/free-regular-svg-icons";
|
import { faEnvelope } from "@fortawesome/free-regular-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { useServerConfig } from "@app/context";
|
||||||
|
import { LoginMethod } from "@app/hooks/api/admin/types";
|
||||||
|
|
||||||
import { Button } from "../v2";
|
import { Button } from "../v2";
|
||||||
|
|
||||||
export default function InitialSignupStep({
|
export default function InitialSignupStep({
|
||||||
@@ -12,67 +15,79 @@ export default function InitialSignupStep({
|
|||||||
setIsSignupWithEmail: (value: boolean) => void;
|
setIsSignupWithEmail: (value: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { config } = useServerConfig();
|
||||||
|
|
||||||
|
const shouldDisplaySignupMethod = (method: LoginMethod) =>
|
||||||
|
!config.enabledLoginMethods || config.enabledLoginMethods.includes(method);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto flex w-full flex-col items-center justify-center">
|
<div 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">
|
<h1 className="mb-8 bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
|
||||||
{t("signup.initial-title")}
|
{t("signup.initial-title")}
|
||||||
</h1>
|
</h1>
|
||||||
<div className="w-1/4 min-w-[20rem] rounded-md lg:w-1/6">
|
{shouldDisplaySignupMethod(LoginMethod.GOOGLE) && (
|
||||||
<Button
|
<div className="w-1/4 min-w-[20rem] rounded-md lg:w-1/6">
|
||||||
colorSchema="primary"
|
<Button
|
||||||
variant="solid"
|
colorSchema="primary"
|
||||||
onClick={() => {
|
variant="solid"
|
||||||
window.open("/api/v1/sso/redirect/google");
|
onClick={() => {
|
||||||
window.close();
|
window.open("/api/v1/sso/redirect/google");
|
||||||
}}
|
window.close();
|
||||||
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
|
}}
|
||||||
className="mx-0 h-12 w-full"
|
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
|
||||||
>
|
className="mx-0 h-12 w-full"
|
||||||
{t("signup.continue-with-google")}
|
>
|
||||||
</Button>
|
{t("signup.continue-with-google")}
|
||||||
</div>
|
</Button>
|
||||||
<div className="mt-4 w-1/4 min-w-[20rem] rounded-md lg:w-1/6">
|
</div>
|
||||||
<Button
|
)}
|
||||||
colorSchema="primary"
|
{shouldDisplaySignupMethod(LoginMethod.GITHUB) && (
|
||||||
variant="outline_bg"
|
<div className="mt-4 w-1/4 min-w-[20rem] rounded-md lg:w-1/6">
|
||||||
onClick={() => {
|
<Button
|
||||||
window.open("/api/v1/sso/redirect/github");
|
colorSchema="primary"
|
||||||
window.close();
|
variant="outline_bg"
|
||||||
}}
|
onClick={() => {
|
||||||
leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />}
|
window.open("/api/v1/sso/redirect/github");
|
||||||
className="mx-0 h-12 w-full"
|
window.close();
|
||||||
>
|
}}
|
||||||
Continue with GitHub
|
leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />}
|
||||||
</Button>
|
className="mx-0 h-12 w-full"
|
||||||
</div>
|
>
|
||||||
<div className="mt-4 w-1/4 min-w-[20rem] rounded-md lg:w-1/6">
|
Continue with GitHub
|
||||||
<Button
|
</Button>
|
||||||
colorSchema="primary"
|
</div>
|
||||||
variant="outline_bg"
|
)}
|
||||||
onClick={() => {
|
{shouldDisplaySignupMethod(LoginMethod.GITLAB) && (
|
||||||
window.open("/api/v1/sso/redirect/gitlab");
|
<div className="mt-4 w-1/4 min-w-[20rem] rounded-md lg:w-1/6">
|
||||||
window.close();
|
<Button
|
||||||
}}
|
colorSchema="primary"
|
||||||
leftIcon={<FontAwesomeIcon icon={faGitlab} className="mr-2" />}
|
variant="outline_bg"
|
||||||
className="mx-0 h-12 w-full"
|
onClick={() => {
|
||||||
>
|
window.open("/api/v1/sso/redirect/gitlab");
|
||||||
Continue with GitLab
|
window.close();
|
||||||
</Button>
|
}}
|
||||||
</div>
|
leftIcon={<FontAwesomeIcon icon={faGitlab} className="mr-2" />}
|
||||||
<div className="mt-4 w-1/4 min-w-[20rem] rounded-md text-center lg:w-1/6">
|
className="mx-0 h-12 w-full"
|
||||||
<Button
|
>
|
||||||
colorSchema="primary"
|
Continue with GitLab
|
||||||
variant="outline_bg"
|
</Button>
|
||||||
onClick={() => {
|
</div>
|
||||||
setIsSignupWithEmail(true);
|
)}
|
||||||
}}
|
{shouldDisplaySignupMethod(LoginMethod.EMAIL) && (
|
||||||
leftIcon={<FontAwesomeIcon icon={faEnvelope} className="mr-2" />}
|
<div className="mt-4 w-1/4 min-w-[20rem] rounded-md text-center lg:w-1/6">
|
||||||
className="mx-0 h-12 w-full"
|
<Button
|
||||||
>
|
colorSchema="primary"
|
||||||
Continue with Email
|
variant="outline_bg"
|
||||||
</Button>
|
onClick={() => {
|
||||||
</div>
|
setIsSignupWithEmail(true);
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faEnvelope} className="mr-2" />}
|
||||||
|
className="mx-0 h-12 w-full"
|
||||||
|
>
|
||||||
|
Continue with Email
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="mt-6 w-1/4 min-w-[20rem] px-8 text-center text-xs text-bunker-400 lg:w-1/6">
|
<div className="mt-6 w-1/4 min-w-[20rem] px-8 text-center text-xs text-bunker-400 lg:w-1/6">
|
||||||
{t("signup.create-policy")}
|
{t("signup.create-policy")}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
export enum LoginMethod {
|
||||||
|
EMAIL = "email",
|
||||||
|
GOOGLE = "google",
|
||||||
|
GITHUB = "github",
|
||||||
|
GITLAB = "gitlab",
|
||||||
|
SAML = "saml",
|
||||||
|
LDAP = "ldap",
|
||||||
|
OIDC = "oidc"
|
||||||
|
}
|
||||||
|
|
||||||
export type TServerConfig = {
|
export type TServerConfig = {
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
allowSignUp: boolean;
|
allowSignUp: boolean;
|
||||||
@@ -9,6 +19,7 @@ export type TServerConfig = {
|
|||||||
isSecretScanningDisabled: boolean;
|
isSecretScanningDisabled: boolean;
|
||||||
defaultAuthOrgSlug: string | null;
|
defaultAuthOrgSlug: string | null;
|
||||||
defaultAuthOrgId: string | null;
|
defaultAuthOrgId: string | null;
|
||||||
|
enabledLoginMethods: LoginMethod[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TCreateAdminUserDTO = {
|
export type TCreateAdminUserDTO = {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { CAPTCHA_SITE_KEY } from "@app/components/utilities/config";
|
|||||||
import { Button, Input } from "@app/components/v2";
|
import { Button, Input } 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 { useNavigateToSelectOrganization } from "../../Login.utils";
|
import { useNavigateToSelectOrganization } from "../../Login.utils";
|
||||||
|
|
||||||
@@ -61,6 +62,9 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const shouldDisplayLoginMethod = (method: LoginMethod) =>
|
||||||
|
!config.enabledLoginMethods || config.enabledLoginMethods.includes(method);
|
||||||
|
|
||||||
const handleLogin = async (e: FormEvent<HTMLFormElement>) => {
|
const handleLogin = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
@@ -162,156 +166,179 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
|||||||
<h1 className="mb-8 bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
|
<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
|
Login to Infisical
|
||||||
</h1>
|
</h1>
|
||||||
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
{shouldDisplayLoginMethod(LoginMethod.GOOGLE) && (
|
||||||
<Button
|
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
colorSchema="primary"
|
<Button
|
||||||
variant="outline_bg"
|
colorSchema="primary"
|
||||||
onClick={() => {
|
variant="outline_bg"
|
||||||
const callbackPort = queryParams.get("callback_port");
|
onClick={() => {
|
||||||
|
const callbackPort = queryParams.get("callback_port");
|
||||||
|
|
||||||
window.open(
|
window.open(
|
||||||
`/api/v1/sso/redirect/google${callbackPort ? `?callback_port=${callbackPort}` : ""}`
|
`/api/v1/sso/redirect/google${callbackPort ? `?callback_port=${callbackPort}` : ""}`
|
||||||
);
|
);
|
||||||
window.close();
|
window.close();
|
||||||
}}
|
}}
|
||||||
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
|
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
|
||||||
className="mx-0 h-10 w-full"
|
className="mx-0 h-10 w-full"
|
||||||
>
|
>
|
||||||
{t("login.continue-with-google")}
|
{t("login.continue-with-google")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
<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={() => {
|
|
||||||
const callbackPort = queryParams.get("callback_port");
|
|
||||||
|
|
||||||
window.open(
|
|
||||||
`/api/v1/sso/redirect/github${callbackPort ? `?callback_port=${callbackPort}` : ""}`
|
|
||||||
);
|
|
||||||
|
|
||||||
window.close();
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />}
|
|
||||||
className="mx-0 h-10 w-full"
|
|
||||||
>
|
|
||||||
Continue with GitHub
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<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={() => {
|
|
||||||
const callbackPort = queryParams.get("callback_port");
|
|
||||||
|
|
||||||
window.open(
|
|
||||||
`/api/v1/sso/redirect/gitlab${callbackPort ? `?callback_port=${callbackPort}` : ""}`
|
|
||||||
);
|
|
||||||
|
|
||||||
window.close();
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faGitlab} className="mr-2" />}
|
|
||||||
className="mx-0 h-10 w-full"
|
|
||||||
>
|
|
||||||
Continue with GitLab
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<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={() => {
|
|
||||||
handleSaml(2);
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
|
||||||
className="mx-0 h-10 w-full"
|
|
||||||
>
|
|
||||||
Continue with SAML
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<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={() => {
|
|
||||||
setStep(3);
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
|
||||||
className="mx-0 h-10 w-full"
|
|
||||||
>
|
|
||||||
Continue with OIDC
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<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={() => {
|
|
||||||
router.push("/login/ldap");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
|
||||||
className="mx-0 h-10 w-full"
|
|
||||||
>
|
|
||||||
Continue with LDAP
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="my-4 flex w-1/4 min-w-[20rem] flex-row items-center py-2 lg:w-1/6">
|
|
||||||
<div className="w-full border-t border-mineshaft-400/60" />
|
|
||||||
<span className="mx-2 text-xs text-mineshaft-200">or</span>
|
|
||||||
<div className="w-full border-t border-mineshaft-400/60" />
|
|
||||||
</div>
|
|
||||||
<div className="w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
|
||||||
<Input
|
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
type="email"
|
|
||||||
placeholder="Enter your email..."
|
|
||||||
isRequired
|
|
||||||
autoComplete="username"
|
|
||||||
className="h-10"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
|
||||||
<Input
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
type="password"
|
|
||||||
placeholder="Enter your password..."
|
|
||||||
isRequired
|
|
||||||
autoComplete="current-password"
|
|
||||||
id="current-password"
|
|
||||||
className="select:-webkit-autofill:focus h-10"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{shouldShowCaptcha && (
|
|
||||||
<div className="mt-4">
|
|
||||||
<HCaptcha
|
|
||||||
theme="dark"
|
|
||||||
sitekey={CAPTCHA_SITE_KEY}
|
|
||||||
onVerify={(token) => setCaptchaToken(token)}
|
|
||||||
ref={captchaRef}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="mt-3 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
{shouldDisplayLoginMethod(LoginMethod.GITHUB) && (
|
||||||
<Button
|
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
disabled={shouldShowCaptcha && captchaToken === ""}
|
<Button
|
||||||
type="submit"
|
colorSchema="primary"
|
||||||
size="sm"
|
variant="outline_bg"
|
||||||
isFullWidth
|
onClick={() => {
|
||||||
className="h-10"
|
const callbackPort = queryParams.get("callback_port");
|
||||||
colorSchema="primary"
|
|
||||||
variant="solid"
|
window.open(
|
||||||
isLoading={isLoading}
|
`/api/v1/sso/redirect/github${callbackPort ? `?callback_port=${callbackPort}` : ""}`
|
||||||
>
|
);
|
||||||
{" "}
|
|
||||||
Continue with Email{" "}
|
window.close();
|
||||||
</Button>
|
}}
|
||||||
</div>
|
leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />}
|
||||||
|
className="mx-0 h-10 w-full"
|
||||||
|
>
|
||||||
|
Continue with GitHub
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{shouldDisplayLoginMethod(LoginMethod.GITLAB) && (
|
||||||
|
<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={() => {
|
||||||
|
const callbackPort = queryParams.get("callback_port");
|
||||||
|
|
||||||
|
window.open(
|
||||||
|
`/api/v1/sso/redirect/gitlab${callbackPort ? `?callback_port=${callbackPort}` : ""}`
|
||||||
|
);
|
||||||
|
|
||||||
|
window.close();
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faGitlab} className="mr-2" />}
|
||||||
|
className="mx-0 h-10 w-full"
|
||||||
|
>
|
||||||
|
Continue with GitLab
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{shouldDisplayLoginMethod(LoginMethod.SAML) && (
|
||||||
|
<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={() => {
|
||||||
|
handleSaml(2);
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
||||||
|
className="mx-0 h-10 w-full"
|
||||||
|
>
|
||||||
|
Continue with SAML
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{shouldDisplayLoginMethod(LoginMethod.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={() => {
|
||||||
|
setStep(3);
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
||||||
|
className="mx-0 h-10 w-full"
|
||||||
|
>
|
||||||
|
Continue with OIDC
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{shouldDisplayLoginMethod(LoginMethod.LDAP) && (
|
||||||
|
<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={() => {
|
||||||
|
router.push("/login/ldap");
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
||||||
|
className="mx-0 h-10 w-full"
|
||||||
|
>
|
||||||
|
Continue with LDAP
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(!config.enabledLoginMethods ||
|
||||||
|
(shouldDisplayLoginMethod(LoginMethod.EMAIL) && config.enabledLoginMethods.length > 1)) && (
|
||||||
|
<div className="my-4 flex w-1/4 min-w-[20rem] flex-row items-center py-2 lg:w-1/6">
|
||||||
|
<div className="w-full border-t border-mineshaft-400/60" />
|
||||||
|
<span className="mx-2 text-xs text-mineshaft-200">or</span>
|
||||||
|
<div className="w-full border-t border-mineshaft-400/60" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{shouldDisplayLoginMethod(LoginMethod.EMAIL) && (
|
||||||
|
<>
|
||||||
|
<div className="w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
|
<Input
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
type="email"
|
||||||
|
placeholder="Enter your email..."
|
||||||
|
isRequired
|
||||||
|
autoComplete="username"
|
||||||
|
className="h-10"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
|
<Input
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
type="password"
|
||||||
|
placeholder="Enter your password..."
|
||||||
|
isRequired
|
||||||
|
autoComplete="current-password"
|
||||||
|
id="current-password"
|
||||||
|
className="select:-webkit-autofill:focus h-10"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{shouldShowCaptcha && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<HCaptcha
|
||||||
|
theme="dark"
|
||||||
|
sitekey={CAPTCHA_SITE_KEY}
|
||||||
|
onVerify={(token) => setCaptchaToken(token)}
|
||||||
|
ref={captchaRef}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="mt-3 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
|
<Button
|
||||||
|
disabled={shouldShowCaptcha && captchaToken === ""}
|
||||||
|
type="submit"
|
||||||
|
size="sm"
|
||||||
|
isFullWidth
|
||||||
|
className="h-10"
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="solid"
|
||||||
|
isLoading={isLoading}
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
Continue with Email{" "}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{!isLoading && loginError && <Error text={t("login.error-login") ?? ""} />}
|
{!isLoading && loginError && <Error text={t("login.error-login") ?? ""} />}
|
||||||
{config.allowSignUp ? (
|
{config.allowSignUp &&
|
||||||
|
(shouldDisplayLoginMethod(LoginMethod.EMAIL) ||
|
||||||
|
shouldDisplayLoginMethod(LoginMethod.GOOGLE) ||
|
||||||
|
shouldDisplayLoginMethod(LoginMethod.GITHUB) ||
|
||||||
|
shouldDisplayLoginMethod(LoginMethod.GITLAB)) ? (
|
||||||
<div className="mt-6 flex flex-row text-sm text-bunker-400">
|
<div className="mt-6 flex flex-row text-sm text-bunker-400">
|
||||||
<Link href="/signup">
|
<Link href="/signup">
|
||||||
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
||||||
@@ -322,13 +349,15 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
|||||||
) : (
|
) : (
|
||||||
<div className="mt-4" />
|
<div className="mt-4" />
|
||||||
)}
|
)}
|
||||||
<div className="mt-2 flex flex-row text-sm text-bunker-400">
|
{shouldDisplayLoginMethod(LoginMethod.EMAIL) && (
|
||||||
<Link href="/verify-email">
|
<div className="mt-2 flex flex-row text-sm text-bunker-400">
|
||||||
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
<Link href="/verify-email">
|
||||||
Forgot password? Recover your account
|
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
||||||
</span>
|
Forgot password? Recover your account
|
||||||
</Link>
|
</span>
|
||||||
</div>
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
import { OrgPermissionActions, OrgPermissionSubjects, useServerConfig } from "@app/context";
|
||||||
import { withPermission } from "@app/hoc";
|
import { withPermission } from "@app/hoc";
|
||||||
|
import { LoginMethod } from "@app/hooks/api/admin/types";
|
||||||
|
|
||||||
import { OrgGeneralAuthSection } from "./OrgGeneralAuthSection";
|
import { OrgGeneralAuthSection } from "./OrgGeneralAuthSection";
|
||||||
import { OrgLDAPSection } from "./OrgLDAPSection";
|
import { OrgLDAPSection } from "./OrgLDAPSection";
|
||||||
@@ -9,12 +10,23 @@ import { OrgSSOSection } from "./OrgSSOSection";
|
|||||||
|
|
||||||
export const OrgAuthTab = withPermission(
|
export const OrgAuthTab = withPermission(
|
||||||
() => {
|
() => {
|
||||||
|
const {
|
||||||
|
config: { enabledLoginMethods }
|
||||||
|
} = useServerConfig();
|
||||||
|
|
||||||
|
const shouldDisplaySection = (method: LoginMethod) =>
|
||||||
|
!enabledLoginMethods || enabledLoginMethods.includes(method);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-6">
|
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-6">
|
||||||
<OrgGeneralAuthSection />
|
{shouldDisplaySection(LoginMethod.SAML) && (
|
||||||
<OrgSSOSection />
|
<>
|
||||||
<OrgOIDCSection />
|
<OrgGeneralAuthSection />
|
||||||
<OrgLDAPSection />
|
<OrgSSOSection />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{shouldDisplaySection(LoginMethod.OIDC) && <OrgOIDCSection />}
|
||||||
|
{shouldDisplaySection(LoginMethod.LDAP) && <OrgLDAPSection />}
|
||||||
<OrgScimSection />
|
<OrgScimSection />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { useLogoutUser, useUpdateOrg } from "@app/hooks/api";
|
|||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
export const OrgGeneralAuthSection = () => {
|
export const OrgGeneralAuthSection = () => {
|
||||||
|
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
||||||
@@ -88,6 +87,7 @@ export const OrgGeneralAuthSection = () => {
|
|||||||
Enforce members to authenticate via SAML to access this organization
|
Enforce members to authenticate via SAML to access this organization
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<hr className="border-mineshaft-600" />
|
||||||
<UpgradePlanModal
|
<UpgradePlanModal
|
||||||
isOpen={popUp.upgradePlan.isOpen}
|
isOpen={popUp.upgradePlan.isOpen}
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ export const OrgLDAPSection = (): JSX.Element => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<hr className="border-mineshaft-600" />
|
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<div className="mb-2 flex items-center justify-between">
|
<div className="mb-2 flex items-center justify-between">
|
||||||
<h2 className="text-md text-mineshaft-100">LDAP</h2>
|
<h2 className="text-md text-mineshaft-100">LDAP</h2>
|
||||||
@@ -152,6 +151,7 @@ export const OrgLDAPSection = (): JSX.Element => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<hr className="border-mineshaft-600" />
|
||||||
<LDAPModal
|
<LDAPModal
|
||||||
popUp={popUp}
|
popUp={popUp}
|
||||||
handlePopUpClose={handlePopUpClose}
|
handlePopUpClose={handlePopUpClose}
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ export const OrgOIDCSection = (): JSX.Element => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<hr className="border-mineshaft-600" />
|
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<div className="mb-2 flex items-center justify-between">
|
<div className="mb-2 flex items-center justify-between">
|
||||||
<h2 className="text-md text-mineshaft-100">OIDC</h2>
|
<h2 className="text-md text-mineshaft-100">OIDC</h2>
|
||||||
@@ -103,6 +102,7 @@ export const OrgOIDCSection = (): JSX.Element => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<hr className="border-mineshaft-600" />
|
||||||
<OIDCModal
|
<OIDCModal
|
||||||
popUp={popUp}
|
popUp={popUp}
|
||||||
handlePopUpClose={handlePopUpClose}
|
handlePopUpClose={handlePopUpClose}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { usePopUp } from "@app/hooks/usePopUp";
|
|||||||
import { ScimTokenModal } from "./ScimTokenModal";
|
import { ScimTokenModal } from "./ScimTokenModal";
|
||||||
|
|
||||||
export const OrgScimSection = () => {
|
export const OrgScimSection = () => {
|
||||||
|
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
||||||
@@ -59,7 +58,6 @@ export const OrgScimSection = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<hr className="border-mineshaft-600" />
|
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<div className="mb-2 flex items-center justify-between">
|
<div className="mb-2 flex items-center justify-between">
|
||||||
<h2 className="text-md text-mineshaft-100">SCIM</h2>
|
<h2 className="text-md text-mineshaft-100">SCIM</h2>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { SSOModal } from "./SSOModal";
|
|||||||
export const OrgSSOSection = (): JSX.Element => {
|
export const OrgSSOSection = (): JSX.Element => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
const { data, isLoading } = useGetSSOConfig(currentOrg?.id ?? "");
|
const { data, isLoading } = useGetSSOConfig(currentOrg?.id ?? "");
|
||||||
const { mutateAsync } = useUpdateSSOConfig();
|
const { mutateAsync } = useUpdateSSOConfig();
|
||||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
@@ -115,6 +115,7 @@ export const OrgSSOSection = (): JSX.Element => {
|
|||||||
Allow members to authenticate into Infisical with SAML
|
Allow members to authenticate into Infisical with SAML
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<hr className="border-mineshaft-600" />
|
||||||
<SSOModal
|
<SSOModal
|
||||||
popUp={popUp}
|
popUp={popUp}
|
||||||
handlePopUpClose={handlePopUpClose}
|
handlePopUpClose={handlePopUpClose}
|
||||||
|
|||||||
@@ -8,23 +8,24 @@ import * as yup from "yup";
|
|||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Switch } from "@app/components/v2";
|
import { Switch } from "@app/components/v2";
|
||||||
import { useUser } from "@app/context";
|
import { useServerConfig, useUser } from "@app/context";
|
||||||
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
||||||
|
import { LoginMethod } from "@app/hooks/api/admin/types";
|
||||||
import { AuthMethod } from "@app/hooks/api/users/types";
|
import { AuthMethod } from "@app/hooks/api/users/types";
|
||||||
|
|
||||||
interface AuthMethodOption {
|
interface AuthMethodOption {
|
||||||
label: string;
|
label: string;
|
||||||
value: AuthMethod;
|
value: AuthMethod;
|
||||||
icon: IconDefinition;
|
icon: IconDefinition;
|
||||||
|
loginMethod: LoginMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
const authMethodOpts: AuthMethodOption[] = [
|
const authMethodOpts: AuthMethodOption[] = [
|
||||||
{ label: "Email", value: AuthMethod.EMAIL, icon: faEnvelope },
|
{ label: "Email", value: AuthMethod.EMAIL, icon: faEnvelope, loginMethod: LoginMethod.EMAIL },
|
||||||
{ label: "Google", value: AuthMethod.GOOGLE, icon: faGoogle },
|
{ label: "Google", value: AuthMethod.GOOGLE, icon: faGoogle, loginMethod: LoginMethod.GOOGLE },
|
||||||
{ label: "GitHub", value: AuthMethod.GITHUB, icon: faGithub },
|
{ label: "GitHub", value: AuthMethod.GITHUB, icon: faGithub, loginMethod: LoginMethod.GITHUB },
|
||||||
{ label: "GitLab", value: AuthMethod.GITLAB, icon: faGitlab }
|
{ label: "GitLab", value: AuthMethod.GITLAB, icon: faGitlab, loginMethod: LoginMethod.GITLAB }
|
||||||
];
|
];
|
||||||
|
|
||||||
const schema = yup.object({
|
const schema = yup.object({
|
||||||
authMethods: yup.array().required("Auth method is required")
|
authMethods: yup.array().required("Auth method is required")
|
||||||
});
|
});
|
||||||
@@ -32,8 +33,8 @@ const schema = yup.object({
|
|||||||
export type FormData = yup.InferType<typeof schema>;
|
export type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
export const AuthMethodSection = () => {
|
export const AuthMethodSection = () => {
|
||||||
|
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
|
const { config } = useServerConfig();
|
||||||
const { mutateAsync } = useUpdateUserAuthMethods();
|
const { mutateAsync } = useUpdateUserAuthMethods();
|
||||||
|
|
||||||
const { reset, setValue, watch } = useForm<FormData>({
|
const { reset, setValue, watch } = useForm<FormData>({
|
||||||
@@ -102,6 +103,14 @@ export const AuthMethodSection = () => {
|
|||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
{user &&
|
{user &&
|
||||||
authMethodOpts.map((authMethodOpt) => {
|
authMethodOpts.map((authMethodOpt) => {
|
||||||
|
// only filter when enabledLoginMethods is explicitly configured by admin
|
||||||
|
if (
|
||||||
|
config.enabledLoginMethods &&
|
||||||
|
!config.enabledLoginMethods.includes(authMethodOpt.loginMethod)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center p-4" key={`auth-method-${authMethodOpt.value}`}>
|
<div className="flex items-center p-4" key={`auth-method-${authMethodOpt.value}`}>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
|
|||||||
252
frontend/src/views/admin/DashboardPage/AuthPanel.tsx
Normal file
252
frontend/src/views/admin/DashboardPage/AuthPanel.tsx
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { Button, FormControl, Switch } from "@app/components/v2";
|
||||||
|
import { useServerConfig } from "@app/context";
|
||||||
|
import { useUpdateServerConfig } from "@app/hooks/api";
|
||||||
|
import { LoginMethod } from "@app/hooks/api/admin/types";
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
isEmailEnabled: z.boolean(),
|
||||||
|
isGoogleEnabled: z.boolean(),
|
||||||
|
isGithubEnabled: z.boolean(),
|
||||||
|
isGitlabEnabled: z.boolean(),
|
||||||
|
isSamlEnabled: z.boolean(),
|
||||||
|
isLdapEnabled: z.boolean(),
|
||||||
|
isOidcEnabled: z.boolean()
|
||||||
|
});
|
||||||
|
|
||||||
|
type TAuthForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
export const AuthPanel = () => {
|
||||||
|
const { config } = useServerConfig();
|
||||||
|
const { enabledLoginMethods } = config;
|
||||||
|
const { mutateAsync: updateServerConfig } = useUpdateServerConfig();
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { isSubmitting, isDirty }
|
||||||
|
} = useForm<TAuthForm>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
// if not yet explicitly defined by the admin, all login methods should be enabled by default
|
||||||
|
values: enabledLoginMethods
|
||||||
|
? {
|
||||||
|
isEmailEnabled: enabledLoginMethods.includes(LoginMethod.EMAIL),
|
||||||
|
isGoogleEnabled: enabledLoginMethods.includes(LoginMethod.GOOGLE),
|
||||||
|
isGithubEnabled: enabledLoginMethods.includes(LoginMethod.GITHUB),
|
||||||
|
isGitlabEnabled: enabledLoginMethods.includes(LoginMethod.GITLAB),
|
||||||
|
isSamlEnabled: enabledLoginMethods.includes(LoginMethod.SAML),
|
||||||
|
isLdapEnabled: enabledLoginMethods.includes(LoginMethod.LDAP),
|
||||||
|
isOidcEnabled: enabledLoginMethods.includes(LoginMethod.OIDC)
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
isEmailEnabled: true,
|
||||||
|
isGoogleEnabled: true,
|
||||||
|
isGithubEnabled: true,
|
||||||
|
isGitlabEnabled: true,
|
||||||
|
isSamlEnabled: true,
|
||||||
|
isLdapEnabled: true,
|
||||||
|
isOidcEnabled: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const onAuthFormSubmit = async (formData: TAuthForm) => {
|
||||||
|
try {
|
||||||
|
const enabledMethods: LoginMethod[] = [];
|
||||||
|
if (formData.isEmailEnabled) {
|
||||||
|
enabledMethods.push(LoginMethod.EMAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.isGoogleEnabled) {
|
||||||
|
enabledMethods.push(LoginMethod.GOOGLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.isGithubEnabled) {
|
||||||
|
enabledMethods.push(LoginMethod.GITHUB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.isGitlabEnabled) {
|
||||||
|
enabledMethods.push(LoginMethod.GITLAB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.isSamlEnabled) {
|
||||||
|
enabledMethods.push(LoginMethod.SAML);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.isLdapEnabled) {
|
||||||
|
enabledMethods.push(LoginMethod.LDAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.isOidcEnabled) {
|
||||||
|
enabledMethods.push(LoginMethod.OIDC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enabledMethods.length) {
|
||||||
|
createNotification({
|
||||||
|
type: "error",
|
||||||
|
text: "At least one login method should be enabled."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateServerConfig({
|
||||||
|
enabledLoginMethods: enabledMethods
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Login methods have been successfully updated.",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
createNotification({
|
||||||
|
type: "error",
|
||||||
|
text: "Failed to update login methods."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
||||||
|
onSubmit={handleSubmit(onAuthFormSubmit)}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col justify-start">
|
||||||
|
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Login Methods</div>
|
||||||
|
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
||||||
|
Select the login methods you wish to allow for all users of this instance.
|
||||||
|
</div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="isEmailEnabled"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
id="email-enabled"
|
||||||
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
|
isChecked={field.value}
|
||||||
|
>
|
||||||
|
<p className="w-24">Email</p>
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="isGoogleEnabled"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
id="google-enabled"
|
||||||
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
|
isChecked={field.value}
|
||||||
|
>
|
||||||
|
<p className="w-24">Google SSO</p>
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="isGithubEnabled"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
id="enable-github"
|
||||||
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
|
isChecked={field.value}
|
||||||
|
>
|
||||||
|
<p className="w-24">Github SSO</p>
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="isGitlabEnabled"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
id="enable-gitlab"
|
||||||
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
|
isChecked={field.value}
|
||||||
|
>
|
||||||
|
<p className="w-24">Gitlab SSO</p>
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="isSamlEnabled"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
id="enable-saml"
|
||||||
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
|
isChecked={field.value}
|
||||||
|
>
|
||||||
|
<p className="w-24">SAML SSO</p>
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="isOidcEnabled"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
id="enable-oidc"
|
||||||
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
|
isChecked={field.value}
|
||||||
|
>
|
||||||
|
<p className="w-24">OIDC SSO</p>
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="isLdapEnabled"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
id="enable-ldap"
|
||||||
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
|
isChecked={field.value}
|
||||||
|
>
|
||||||
|
<p className="w-24">LDAP</p>
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className="mt-2"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting || !isDirty}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -24,10 +24,12 @@ import {
|
|||||||
import { useOrganization, useServerConfig, useUser } from "@app/context";
|
import { useOrganization, useServerConfig, useUser } from "@app/context";
|
||||||
import { useGetOrganizations, useUpdateServerConfig } from "@app/hooks/api";
|
import { useGetOrganizations, useUpdateServerConfig } from "@app/hooks/api";
|
||||||
|
|
||||||
|
import { AuthPanel } from "./AuthPanel";
|
||||||
import { RateLimitPanel } from "./RateLimitPanel";
|
import { RateLimitPanel } from "./RateLimitPanel";
|
||||||
|
|
||||||
enum TabSections {
|
enum TabSections {
|
||||||
Settings = "settings",
|
Settings = "settings",
|
||||||
|
Auth = "auth",
|
||||||
RateLimit = "rate-limit"
|
RateLimit = "rate-limit"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +122,7 @@ export const AdminDashboardPage = () => {
|
|||||||
<div className="mx-auto mb-6 w-full max-w-7xl pt-6">
|
<div className="mx-auto mb-6 w-full max-w-7xl pt-6">
|
||||||
<div className="mb-8 flex flex-col items-start justify-between text-xl">
|
<div className="mb-8 flex flex-col items-start justify-between text-xl">
|
||||||
<h1 className="text-3xl font-semibold">Admin Dashboard</h1>
|
<h1 className="text-3xl font-semibold">Admin Dashboard</h1>
|
||||||
<p className="text-base text-bunker-300">Manage your Infisical instance.</p>
|
<p className="text-base text-bunker-300">Manage your instance level configurations.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isUserLoading || isNotAllowed ? (
|
{isUserLoading || isNotAllowed ? (
|
||||||
@@ -131,6 +133,7 @@ export const AdminDashboardPage = () => {
|
|||||||
<TabList>
|
<TabList>
|
||||||
<div className="flex w-full flex-row border-b border-mineshaft-600">
|
<div className="flex w-full flex-row border-b border-mineshaft-600">
|
||||||
<Tab value={TabSections.Settings}>General</Tab>
|
<Tab value={TabSections.Settings}>General</Tab>
|
||||||
|
<Tab value={TabSections.Auth}>Authentication</Tab>
|
||||||
<Tab value={TabSections.RateLimit}>Rate Limit</Tab>
|
<Tab value={TabSections.RateLimit}>Rate Limit</Tab>
|
||||||
</div>
|
</div>
|
||||||
</TabList>
|
</TabList>
|
||||||
@@ -203,7 +206,8 @@ 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 selected, user logins will be automatically scoped to the selected organization.
|
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.
|
||||||
</div>
|
</div>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -310,6 +314,9 @@ export const AdminDashboardPage = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
<TabPanel value={TabSections.Auth}>
|
||||||
|
<AuthPanel />
|
||||||
|
</TabPanel>
|
||||||
<TabPanel value={TabSections.RateLimit}>
|
<TabPanel value={TabSections.RateLimit}>
|
||||||
<RateLimitPanel />
|
<RateLimitPanel />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|||||||
Reference in New Issue
Block a user