From 98a15a901ec17c38c9312ce0e5196b45524475ba Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Wed, 26 Jun 2024 22:45:14 +0800
Subject: [PATCH 01/13] feat: allowed toggling login options as admin
---
...0240626115035_admin-login-method-config.ts | 19 ++
backend/src/db/schemas/super-admin.ts | 3 +-
backend/src/server/routes/v1/admin-router.ts | 10 +-
.../services/super-admin/super-admin-types.ts | 10 +
frontend/src/hooks/api/admin/types.ts | 11 +
.../components/InitialStep/InitialStep.tsx | 321 ++++++++++--------
.../views/admin/DashboardPage/AuthPanel.tsx | 251 ++++++++++++++
.../admin/DashboardPage/DashboardPage.tsx | 9 +-
8 files changed, 488 insertions(+), 146 deletions(-)
create mode 100644 backend/src/db/migrations/20240626115035_admin-login-method-config.ts
create mode 100644 frontend/src/views/admin/DashboardPage/AuthPanel.tsx
diff --git a/backend/src/db/migrations/20240626115035_admin-login-method-config.ts b/backend/src/db/migrations/20240626115035_admin-login-method-config.ts
new file mode 100644
index 0000000000..8748fe753e
--- /dev/null
+++ b/backend/src/db/migrations/20240626115035_admin-login-method-config.ts
@@ -0,0 +1,19 @@
+import { Knex } from "knex";
+
+import { TableName } from "../schemas";
+
+export async function up(knex: Knex): Promise {
+ 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 {
+ if (await knex.schema.hasColumn(TableName.SuperAdmin, "enabledLoginMethods")) {
+ await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
+ t.dropColumn("enabledLoginMethods");
+ });
+ }
+}
diff --git a/backend/src/db/schemas/super-admin.ts b/backend/src/db/schemas/super-admin.ts
index 29e41c78ef..b676b81a80 100644
--- a/backend/src/db/schemas/super-admin.ts
+++ b/backend/src/db/schemas/super-admin.ts
@@ -18,7 +18,8 @@ export const SuperAdminSchema = z.object({
trustSamlEmails: z.boolean().default(false).nullable().optional(),
trustLdapEmails: 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;
diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts
index 24c7e2a6e4..4557152bdb 100644
--- a/backend/src/server/routes/v1/admin-router.ts
+++ b/backend/src/server/routes/v1/admin-router.ts
@@ -8,6 +8,7 @@ import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
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";
export const registerAdminRouter = async (server: FastifyZodProvider) => {
@@ -54,7 +55,14 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
trustSamlEmails: z.boolean().optional(),
trustLdapEmails: 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: {
200: z.object({
diff --git a/backend/src/services/super-admin/super-admin-types.ts b/backend/src/services/super-admin/super-admin-types.ts
index e444c8843a..f622c8f174 100644
--- a/backend/src/services/super-admin/super-admin-types.ts
+++ b/backend/src/services/super-admin/super-admin-types.ts
@@ -15,3 +15,13 @@ export type TAdminSignUpDTO = {
ip: string;
userAgent: string;
};
+
+export enum LoginMethod {
+ EMAIL = "email",
+ GOOGLE = "google",
+ GITHUB = "github",
+ GITLAB = "gitlab",
+ SAML = "saml",
+ LDAP = "ldap",
+ OIDC = "oidc"
+}
diff --git a/frontend/src/hooks/api/admin/types.ts b/frontend/src/hooks/api/admin/types.ts
index 524bc6ace0..4d5add1e0e 100644
--- a/frontend/src/hooks/api/admin/types.ts
+++ b/frontend/src/hooks/api/admin/types.ts
@@ -1,3 +1,13 @@
+export enum LoginMethod {
+ EMAIL = "email",
+ GOOGLE = "google",
+ GITHUB = "github",
+ GITLAB = "gitlab",
+ SAML = "saml",
+ LDAP = "ldap",
+ OIDC = "oidc"
+}
+
export type TServerConfig = {
initialized: boolean;
allowSignUp: boolean;
@@ -9,6 +19,7 @@ export type TServerConfig = {
isSecretScanningDisabled: boolean;
defaultAuthOrgSlug: string | null;
defaultAuthOrgId: string | null;
+ enabledLoginMethods: LoginMethod[];
};
export type TCreateAdminUserDTO = {
diff --git a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
index 0616ee58d1..83615ccf56 100644
--- a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
+++ b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
@@ -15,6 +15,7 @@ import { CAPTCHA_SITE_KEY } from "@app/components/utilities/config";
import { Button, Input } from "@app/components/v2";
import { useServerConfig } from "@app/context";
import { useFetchServerStatus } from "@app/hooks/api";
+import { LoginMethod } from "@app/hooks/api/admin/types";
import { useNavigateToSelectOrganization } from "../../Login.utils";
@@ -162,154 +163,188 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
Login to Infisical
-
-
{
- const callbackPort = queryParams.get("callback_port");
+ {!config.enabledLoginMethods ||
+ (config.enabledLoginMethods.includes(LoginMethod.GOOGLE) && (
+
+ {
+ const callbackPort = queryParams.get("callback_port");
- window.open(
- `/api/v1/sso/redirect/google${callbackPort ? `?callback_port=${callbackPort}` : ""}`
- );
- window.close();
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- {t("login.continue-with-google")}
-
-
-
- {
- const callbackPort = queryParams.get("callback_port");
+ window.open(
+ `/api/v1/sso/redirect/google${
+ callbackPort ? `?callback_port=${callbackPort}` : ""
+ }`
+ );
+ window.close();
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ {t("login.continue-with-google")}
+
+
+ ))}
+ {!config.enabledLoginMethods ||
+ (config.enabledLoginMethods.includes(LoginMethod.GITHUB) && (
+
+ {
+ const callbackPort = queryParams.get("callback_port");
- window.open(
- `/api/v1/sso/redirect/github${callbackPort ? `?callback_port=${callbackPort}` : ""}`
- );
+ window.open(
+ `/api/v1/sso/redirect/github${
+ callbackPort ? `?callback_port=${callbackPort}` : ""
+ }`
+ );
- window.close();
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- Continue with GitHub
-
-
-
- {
- const callbackPort = queryParams.get("callback_port");
+ window.close();
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ Continue with GitHub
+
+
+ ))}
+ {!config.enabledLoginMethods ||
+ (config.enabledLoginMethods.includes(LoginMethod.GITLAB) && (
+
+ {
+ const callbackPort = queryParams.get("callback_port");
- window.open(
- `/api/v1/sso/redirect/gitlab${callbackPort ? `?callback_port=${callbackPort}` : ""}`
- );
+ window.open(
+ `/api/v1/sso/redirect/gitlab${
+ callbackPort ? `?callback_port=${callbackPort}` : ""
+ }`
+ );
- window.close();
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- Continue with GitLab
-
-
-
- {
- handleSaml(2);
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- Continue with SAML
-
-
-
- {
- setStep(3);
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- Continue with OIDC
-
-
-
- {
- router.push("/login/ldap");
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- Continue with LDAP
-
-
-
-
- setEmail(e.target.value)}
- type="email"
- placeholder="Enter your email..."
- isRequired
- autoComplete="username"
- className="h-10"
- />
-
-
- setPassword(e.target.value)}
- type="password"
- placeholder="Enter your password..."
- isRequired
- autoComplete="current-password"
- id="current-password"
- className="select:-webkit-autofill:focus h-10"
- />
-
- {shouldShowCaptcha && (
-
- setCaptchaToken(token)}
- ref={captchaRef}
- />
-
- )}
-
-
- {" "}
- Continue with Email{" "}
-
-
+ window.close();
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ Continue with GitLab
+
+
+ ))}
+ {!config.enabledLoginMethods ||
+ (config.enabledLoginMethods.includes(LoginMethod.SAML) && (
+
+ {
+ handleSaml(2);
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ Continue with SAML
+
+
+ ))}
+ {!config.enabledLoginMethods ||
+ (config.enabledLoginMethods.includes(LoginMethod.OIDC) && (
+
+ {
+ setStep(3);
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ Continue with OIDC
+
+
+ ))}
+ {!config.enabledLoginMethods ||
+ (config.enabledLoginMethods.includes(LoginMethod.LDAP) && (
+
+ {
+ router.push("/login/ldap");
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ Continue with LDAP
+
+
+ ))}
+
+ {!config.enabledLoginMethods ||
+ (config.enabledLoginMethods.length > 1 &&
+ config.enabledLoginMethods.includes(LoginMethod.EMAIL) && (
+
+ ))}
+ {!config.enabledLoginMethods ||
+ (config.enabledLoginMethods.includes(LoginMethod.EMAIL) && (
+ <>
+
+ setEmail(e.target.value)}
+ type="email"
+ placeholder="Enter your email..."
+ isRequired
+ autoComplete="username"
+ className="h-10"
+ />
+
+
+ setPassword(e.target.value)}
+ type="password"
+ placeholder="Enter your password..."
+ isRequired
+ autoComplete="current-password"
+ id="current-password"
+ className="select:-webkit-autofill:focus h-10"
+ />
+
+ {shouldShowCaptcha && (
+
+ setCaptchaToken(token)}
+ ref={captchaRef}
+ />
+
+ )}
+
+
+ {" "}
+ Continue with Email{" "}
+
+
+ >
+ ))}
{!isLoading && loginError && }
{config.allowSignUp ? (
diff --git a/frontend/src/views/admin/DashboardPage/AuthPanel.tsx b/frontend/src/views/admin/DashboardPage/AuthPanel.tsx
new file mode 100644
index 0000000000..0060cb095a
--- /dev/null
+++ b/frontend/src/views/admin/DashboardPage/AuthPanel.tsx
@@ -0,0 +1,251 @@
+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
;
+
+export const AuthPanel = () => {
+ const { config } = useServerConfig();
+ const { enabledLoginMethods } = config;
+ const { mutateAsync: updateServerConfig } = useUpdateServerConfig();
+
+ const {
+ control,
+ handleSubmit,
+ formState: { isSubmitting, isDirty }
+ } = useForm({
+ resolver: zodResolver(formSchema),
+ 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 (
+
+ );
+};
diff --git a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx
index 134a67f8cd..bf8a5a4a9e 100644
--- a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx
+++ b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx
@@ -24,10 +24,12 @@ import {
import { useOrganization, useServerConfig, useUser } from "@app/context";
import { useGetOrganizations, useUpdateServerConfig } from "@app/hooks/api";
+import { AuthPanel } from "./AuthPanel";
import { RateLimitPanel } from "./RateLimitPanel";
enum TabSections {
Settings = "settings",
+ Auth = "auth",
RateLimit = "rate-limit"
}
@@ -131,6 +133,7 @@ export const AdminDashboardPage = () => {
General
+ Auth
Rate Limit
@@ -203,7 +206,8 @@ export const AdminDashboardPage = () => {
Default 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.
+ 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.
{
+
+
+
From e1ed37c7135caf3e916bcbd9d56e182bdb44ab4d Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Thu, 27 Jun 2024 01:07:28 +0800
Subject: [PATCH 02/13] misc: adjusted OrgSettingsPage and PersonalSettingsPage
to include toggle
---
.../components/OrgAuthTab/OrgAuthTab.tsx | 26 +++++++++++++++----
.../OrgAuthTab/OrgGeneralAuthSection.tsx | 2 +-
.../components/OrgAuthTab/OrgLDAPSection.tsx | 2 +-
.../components/OrgAuthTab/OrgOIDCSection.tsx | 2 +-
.../components/OrgAuthTab/OrgSSOSection.tsx | 3 ++-
.../AuthMethodSection/AuthMethodSection.tsx | 23 +++++++++++-----
6 files changed, 42 insertions(+), 16 deletions(-)
diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx
index 31d6987bd8..b1a079922f 100644
--- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx
+++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx
@@ -1,5 +1,8 @@
-import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
+import { useCallback } from "react";
+
+import { OrgPermissionActions, OrgPermissionSubjects, useServerConfig } from "@app/context";
import { withPermission } from "@app/hoc";
+import { LoginMethod } from "@app/hooks/api/admin/types";
import { OrgGeneralAuthSection } from "./OrgGeneralAuthSection";
import { OrgLDAPSection } from "./OrgLDAPSection";
@@ -9,12 +12,25 @@ import { OrgSSOSection } from "./OrgSSOSection";
export const OrgAuthTab = withPermission(
() => {
+ const {
+ config: { enabledLoginMethods }
+ } = useServerConfig();
+
+ const shouldDisplaySection = useCallback(
+ (method: LoginMethod) => !enabledLoginMethods || enabledLoginMethods.includes(method),
+ [enabledLoginMethods]
+ );
+
return (
-
-
-
-
+ {shouldDisplaySection(LoginMethod.SAML) && (
+ <>
+
+
+ >
+ )}
+ {shouldDisplaySection(LoginMethod.OIDC) && }
+ {shouldDisplaySection(LoginMethod.LDAP) && }
);
diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgGeneralAuthSection.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgGeneralAuthSection.tsx
index 7c3af9493f..6e4eca456b 100644
--- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgGeneralAuthSection.tsx
+++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgGeneralAuthSection.tsx
@@ -11,7 +11,6 @@ import { useLogoutUser, useUpdateOrg } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
export const OrgGeneralAuthSection = () => {
-
const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
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
+
handlePopUpToggle("upgradePlan", isOpen)}
diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgLDAPSection.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgLDAPSection.tsx
index 87d0f28aff..0d34fcefc0 100644
--- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgLDAPSection.tsx
+++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgLDAPSection.tsx
@@ -94,7 +94,6 @@ export const OrgLDAPSection = (): JSX.Element => {
return (
<>
-
LDAP
@@ -151,6 +150,7 @@ export const OrgLDAPSection = (): JSX.Element => {
)}
+
{
return (
<>
-
OIDC
@@ -103,6 +102,7 @@ export const OrgOIDCSection = (): JSX.Element => {
)}
+
{
const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
-
+
const { data, isLoading } = useGetSSOConfig(currentOrg?.id ?? "");
const { mutateAsync } = useUpdateSSOConfig();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
@@ -115,6 +115,7 @@ export const OrgSSOSection = (): JSX.Element => {
Allow members to authenticate into Infisical with SAML
+
;
export const AuthMethodSection = () => {
-
const { user } = useUser();
+ const { config } = useServerConfig();
const { mutateAsync } = useUpdateUserAuthMethods();
const { reset, setValue, watch } = useForm({
@@ -102,6 +103,14 @@ export const AuthMethodSection = () => {
{user &&
authMethodOpts.map((authMethodOpt) => {
+ // only filter when enabledLoginMethods is explicitly configured by admin
+ if (
+ config.enabledLoginMethods &&
+ !config.enabledLoginMethods.includes(authMethodOpt.loginMethod)
+ ) {
+ return null;
+ }
+
return (
From 9daa5badec8657f8fd0b8e3f8f129824e30bcd65 Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Thu, 27 Jun 2024 01:15:50 +0800
Subject: [PATCH 03/13] misc: made reusable helper for login page
---
.../components/InitialStep/InitialStep.tsx | 329 +++++++++---------
1 file changed, 160 insertions(+), 169 deletions(-)
diff --git a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
index 83615ccf56..d97126f671 100644
--- a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
+++ b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
@@ -62,6 +62,12 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
}
}, []);
+ const shouldDisplayLoginMethod = useCallback(
+ (method: LoginMethod) =>
+ !config.enabledLoginMethods || config.enabledLoginMethods.includes(method),
+ [config]
+ );
+
const handleLogin = async (e: FormEvent) => {
e.preventDefault();
try {
@@ -163,188 +169,173 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
Login to Infisical
- {!config.enabledLoginMethods ||
- (config.enabledLoginMethods.includes(LoginMethod.GOOGLE) && (
-
-
{
- const callbackPort = queryParams.get("callback_port");
+ {shouldDisplayLoginMethod(LoginMethod.GOOGLE) && (
+
+ {
+ const callbackPort = queryParams.get("callback_port");
- window.open(
- `/api/v1/sso/redirect/google${
- callbackPort ? `?callback_port=${callbackPort}` : ""
- }`
- );
- window.close();
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- {t("login.continue-with-google")}
-
-
- ))}
- {!config.enabledLoginMethods ||
- (config.enabledLoginMethods.includes(LoginMethod.GITHUB) && (
-
- {
- const callbackPort = queryParams.get("callback_port");
+ window.open(
+ `/api/v1/sso/redirect/google${callbackPort ? `?callback_port=${callbackPort}` : ""}`
+ );
+ window.close();
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ {t("login.continue-with-google")}
+
+
+ )}
+ {shouldDisplayLoginMethod(LoginMethod.GITHUB) && (
+
+ {
+ const callbackPort = queryParams.get("callback_port");
- window.open(
- `/api/v1/sso/redirect/github${
- callbackPort ? `?callback_port=${callbackPort}` : ""
- }`
- );
+ window.open(
+ `/api/v1/sso/redirect/github${callbackPort ? `?callback_port=${callbackPort}` : ""}`
+ );
- window.close();
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- Continue with GitHub
-
-
- ))}
- {!config.enabledLoginMethods ||
- (config.enabledLoginMethods.includes(LoginMethod.GITLAB) && (
-
- {
- const callbackPort = queryParams.get("callback_port");
+ window.close();
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ Continue with GitHub
+
+
+ )}
+ {shouldDisplayLoginMethod(LoginMethod.GITLAB) && (
+
+ {
+ const callbackPort = queryParams.get("callback_port");
- window.open(
- `/api/v1/sso/redirect/gitlab${
- callbackPort ? `?callback_port=${callbackPort}` : ""
- }`
- );
+ window.open(
+ `/api/v1/sso/redirect/gitlab${callbackPort ? `?callback_port=${callbackPort}` : ""}`
+ );
- window.close();
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- Continue with GitLab
-
-
- ))}
- {!config.enabledLoginMethods ||
- (config.enabledLoginMethods.includes(LoginMethod.SAML) && (
-
- {
- handleSaml(2);
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- Continue with SAML
-
+ window.close();
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ Continue with GitLab
+
+
+ )}
+ {shouldDisplayLoginMethod(LoginMethod.SAML) && (
+
+ {
+ handleSaml(2);
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ Continue with SAML
+
+
+ )}
+ {shouldDisplayLoginMethod(LoginMethod.OIDC) && (
+
+ {
+ setStep(3);
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ Continue with OIDC
+
+
+ )}
+ {shouldDisplayLoginMethod(LoginMethod.LDAP) && (
+
+ {
+ router.push("/login/ldap");
+ }}
+ leftIcon={ }
+ className="mx-0 h-10 w-full"
+ >
+ Continue with LDAP
+
+
+ )}
+
+ {shouldDisplayLoginMethod(LoginMethod.EMAIL) && config.enabledLoginMethods.length > 1 && (
+
+ )}
+ {shouldDisplayLoginMethod(LoginMethod.EMAIL) && (
+ <>
+
+ setEmail(e.target.value)}
+ type="email"
+ placeholder="Enter your email..."
+ isRequired
+ autoComplete="username"
+ className="h-10"
+ />
- ))}
- {!config.enabledLoginMethods ||
- (config.enabledLoginMethods.includes(LoginMethod.OIDC) && (
- {
- setStep(3);
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
- >
- Continue with OIDC
-
+ setPassword(e.target.value)}
+ type="password"
+ placeholder="Enter your password..."
+ isRequired
+ autoComplete="current-password"
+ id="current-password"
+ className="select:-webkit-autofill:focus h-10"
+ />
- ))}
- {!config.enabledLoginMethods ||
- (config.enabledLoginMethods.includes(LoginMethod.LDAP) && (
-
+ {shouldShowCaptcha && (
+
+ setCaptchaToken(token)}
+ ref={captchaRef}
+ />
+
+ )}
+
{
- router.push("/login/ldap");
- }}
- leftIcon={ }
- className="mx-0 h-10 w-full"
+ variant="solid"
+ isLoading={isLoading}
>
- Continue with LDAP
+ {" "}
+ Continue with Email{" "}
- ))}
-
- {!config.enabledLoginMethods ||
- (config.enabledLoginMethods.length > 1 &&
- config.enabledLoginMethods.includes(LoginMethod.EMAIL) && (
-
- ))}
- {!config.enabledLoginMethods ||
- (config.enabledLoginMethods.includes(LoginMethod.EMAIL) && (
- <>
-
- setEmail(e.target.value)}
- type="email"
- placeholder="Enter your email..."
- isRequired
- autoComplete="username"
- className="h-10"
- />
-
-
- setPassword(e.target.value)}
- type="password"
- placeholder="Enter your password..."
- isRequired
- autoComplete="current-password"
- id="current-password"
- className="select:-webkit-autofill:focus h-10"
- />
-
- {shouldShowCaptcha && (
-
- setCaptchaToken(token)}
- ref={captchaRef}
- />
-
- )}
-
-
- {" "}
- Continue with Email{" "}
-
-
- >
- ))}
+ >
+ )}
{!isLoading && loginError &&
}
{config.allowSignUp ? (
From db39d03713403206725d1e2d0344020ab648074f Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Thu, 27 Jun 2024 01:59:02 +0800
Subject: [PATCH 04/13] misc: added check to backend
---
.../src/services/auth/auth-login-service.ts | 35 +++++++++++++++++++
.../components/InitialStep/InitialStep.tsx | 4 +--
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts
index 8090b937ab..8207e4bec6 100644
--- a/backend/src/services/auth/auth-login-service.ts
+++ b/backend/src/services/auth/auth-login-service.ts
@@ -17,6 +17,7 @@ import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
import { TokenType } from "../auth-token/auth-token-types";
import { TOrgDALFactory } from "../org/org-dal";
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
+import { LoginMethod } from "../super-admin/super-admin-types";
import { TUserDALFactory } from "../user/user-dal";
import { enforceUserLockStatus, validateProviderAuthToken } from "./auth-fns";
import {
@@ -507,6 +508,40 @@ export const authLoginServiceFactory = ({
let user = await userDAL.findUserByUsername(email);
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.",
+ name: "Oauth 2 login"
+ });
+ }
+ break;
+ }
+ case AuthMethod.GOOGLE: {
+ if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GOOGLE)) {
+ throw new BadRequestError({
+ message: "Login with Google is disabled.",
+ name: "Oauth 2 login"
+ });
+ }
+ break;
+ }
+ case AuthMethod.GITLAB: {
+ if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GITLAB)) {
+ throw new BadRequestError({
+ message: "Login with Gitlab is disabled.",
+ name: "Oauth 2 login"
+ });
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
const appCfg = getConfig();
if (!user) {
diff --git a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
index d97126f671..99e80048af 100644
--- a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
+++ b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
@@ -276,8 +276,8 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
)}
-
- {shouldDisplayLoginMethod(LoginMethod.EMAIL) && config.enabledLoginMethods.length > 1 && (
+ {(!config.enabledLoginMethods ||
+ (shouldDisplayLoginMethod(LoginMethod.EMAIL) && config.enabledLoginMethods.length > 1)) && (
or
From c460f226650313ad1ff271d2113829d9cc14966f Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Thu, 27 Jun 2024 12:40:56 +0800
Subject: [PATCH 05/13] misc: added backend disable checks
---
.../ee/services/ldap-config/ldap-config-service.ts | 8 ++++++++
backend/src/ee/services/oidc/oidc-config-service.ts | 8 ++++++++
.../ee/services/saml-config/saml-config-service.ts | 8 ++++++++
backend/src/services/auth/auth-login-service.ts | 13 +++++++++++++
4 files changed, 37 insertions(+)
diff --git a/backend/src/ee/services/ldap-config/ldap-config-service.ts b/backend/src/ee/services/ldap-config/ldap-config-service.ts
index 63565d94c2..7434862fc7 100644
--- a/backend/src/ee/services/ldap-config/ldap-config-service.ts
+++ b/backend/src/ee/services/ldap-config/ldap-config-service.ts
@@ -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 { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-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 { normalizeUsername } from "@app/services/user/user-fns";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
@@ -411,6 +412,13 @@ export const ldapConfigServiceFactory = ({
}: TLdapLoginDTO) => {
const appCfg = getConfig();
const serverCfg = await getServerCfg();
+
+ if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.LDAP)) {
+ throw new BadRequestError({
+ message: "Login with LDAP is disabled."
+ });
+ }
+
let userAlias = await userAliasDAL.findOne({
externalId,
orgId,
diff --git a/backend/src/ee/services/oidc/oidc-config-service.ts b/backend/src/ee/services/oidc/oidc-config-service.ts
index b983492bc2..58b098522c 100644
--- a/backend/src/ee/services/oidc/oidc-config-service.ts
+++ b/backend/src/ee/services/oidc/oidc-config-service.ts
@@ -26,6 +26,7 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-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 { normalizeUsername } from "@app/services/user/user-fns";
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 serverCfg = await getServerCfg();
+
+ if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.OIDC)) {
+ throw new BadRequestError({
+ message: "Login with OIDC is disabled."
+ });
+ }
+
const appCfg = getConfig();
const userAlias = await userAliasDAL.findOne({
externalId,
diff --git a/backend/src/ee/services/saml-config/saml-config-service.ts b/backend/src/ee/services/saml-config/saml-config-service.ts
index 3cc51e1c20..286a85746d 100644
--- a/backend/src/ee/services/saml-config/saml-config-service.ts
+++ b/backend/src/ee/services/saml-config/saml-config-service.ts
@@ -28,6 +28,7 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-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 { normalizeUsername } from "@app/services/user/user-fns";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
@@ -335,6 +336,13 @@ export const samlConfigServiceFactory = ({
}: TSamlLoginDTO) => {
const appCfg = getConfig();
const serverCfg = await getServerCfg();
+
+ if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.SAML)) {
+ throw new BadRequestError({
+ message: "Login with SAML is disabled."
+ });
+ }
+
const userAlias = await userAliasDAL.findOne({
externalId,
orgId,
diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts
index 8207e4bec6..24ae087b7a 100644
--- a/backend/src/services/auth/auth-login-service.ts
+++ b/backend/src/services/auth/auth-login-service.ts
@@ -159,9 +159,22 @@ export const authLoginServiceFactory = ({
const userEnc = await userDAL.findUserEncKeyByUsername({
username: email
});
+ const serverCfg = await getServerCfg();
+
+ if (
+ serverCfg.enabledLoginMethods &&
+ !serverCfg.enabledLoginMethods.includes(LoginMethod.EMAIL) &&
+ !providerAuthToken
+ ) {
+ throw new BadRequestError({
+ message: "Login with email is disabled."
+ });
+ }
+
if (!userEnc || (userEnc && !userEnc.isAccepted)) {
throw new Error("Failed to find user");
}
+
if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) {
validateProviderAuthToken(providerAuthToken as string, email);
}
From b3463e0d0fe563ccc8f5c4a744aaf618d1c0020f Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Thu, 27 Jun 2024 12:55:39 +0800
Subject: [PATCH 06/13] misc: added explicit comment of intent
---
frontend/src/views/admin/DashboardPage/AuthPanel.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/frontend/src/views/admin/DashboardPage/AuthPanel.tsx b/frontend/src/views/admin/DashboardPage/AuthPanel.tsx
index 0060cb095a..f2fab83ec1 100644
--- a/frontend/src/views/admin/DashboardPage/AuthPanel.tsx
+++ b/frontend/src/views/admin/DashboardPage/AuthPanel.tsx
@@ -31,6 +31,7 @@ export const AuthPanel = () => {
formState: { isSubmitting, isDirty }
} = useForm({
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),
From 92a663a17d3f1f3879ea45e3c80d1778148c041c Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Thu, 27 Jun 2024 13:24:26 +0800
Subject: [PATCH 07/13] misc: design change to finalize scim section in org
settings
---
.../OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx
index 601643db80..ffec3f3558 100644
--- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx
+++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx
@@ -13,7 +13,6 @@ import { usePopUp } from "@app/hooks/usePopUp";
import { ScimTokenModal } from "./ScimTokenModal";
export const OrgScimSection = () => {
-
const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
@@ -59,7 +58,6 @@ export const OrgScimSection = () => {
return (
<>
-
SCIM
From d6881e2e6831429ded24526e21ff210812e71af9 Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Thu, 27 Jun 2024 13:53:12 +0800
Subject: [PATCH 08/13] misc: added signup option filtering
---
.../components/signup/InitialSignupStep.tsx | 129 ++++++++++--------
1 file changed, 74 insertions(+), 55 deletions(-)
diff --git a/frontend/src/components/signup/InitialSignupStep.tsx b/frontend/src/components/signup/InitialSignupStep.tsx
index e5c23f3334..8df6759132 100644
--- a/frontend/src/components/signup/InitialSignupStep.tsx
+++ b/frontend/src/components/signup/InitialSignupStep.tsx
@@ -1,9 +1,13 @@
+import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import Link from "next/link";
import { faGithub, faGitlab, faGoogle } from "@fortawesome/free-brands-svg-icons";
import { faEnvelope } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { useServerConfig } from "@app/context";
+import { LoginMethod } from "@app/hooks/api/admin/types";
+
import { Button } from "../v2";
export default function InitialSignupStep({
@@ -12,67 +16,82 @@ export default function InitialSignupStep({
setIsSignupWithEmail: (value: boolean) => void;
}) {
const { t } = useTranslation();
+ const { config } = useServerConfig();
+
+ const shouldDisplaySignupMethod = useCallback(
+ (method: LoginMethod) =>
+ !config.enabledLoginMethods || config.enabledLoginMethods.includes(method),
+ [config]
+ );
return (
{t("signup.initial-title")}
-
- {
- window.open("/api/v1/sso/redirect/google");
- window.close();
- }}
- leftIcon={ }
- className="mx-0 h-12 w-full"
- >
- {t("signup.continue-with-google")}
-
-
-
- {
- window.open("/api/v1/sso/redirect/github");
- window.close();
- }}
- leftIcon={ }
- className="mx-0 h-12 w-full"
- >
- Continue with GitHub
-
-
-
- {
- window.open("/api/v1/sso/redirect/gitlab");
- window.close();
- }}
- leftIcon={ }
- className="mx-0 h-12 w-full"
- >
- Continue with GitLab
-
-
-
- {
- setIsSignupWithEmail(true);
- }}
- leftIcon={ }
- className="mx-0 h-12 w-full"
- >
- Continue with Email
-
-
+ {shouldDisplaySignupMethod(LoginMethod.GOOGLE) && (
+
+ {
+ window.open("/api/v1/sso/redirect/google");
+ window.close();
+ }}
+ leftIcon={ }
+ className="mx-0 h-12 w-full"
+ >
+ {t("signup.continue-with-google")}
+
+
+ )}
+ {shouldDisplaySignupMethod(LoginMethod.GITHUB) && (
+
+ {
+ window.open("/api/v1/sso/redirect/github");
+ window.close();
+ }}
+ leftIcon={ }
+ className="mx-0 h-12 w-full"
+ >
+ Continue with GitHub
+
+
+ )}
+ {shouldDisplaySignupMethod(LoginMethod.GITLAB) && (
+
+ {
+ window.open("/api/v1/sso/redirect/gitlab");
+ window.close();
+ }}
+ leftIcon={ }
+ className="mx-0 h-12 w-full"
+ >
+ Continue with GitLab
+
+
+ )}
+ {shouldDisplaySignupMethod(LoginMethod.EMAIL) && (
+
+ {
+ setIsSignupWithEmail(true);
+ }}
+ leftIcon={ }
+ className="mx-0 h-12 w-full"
+ >
+ Continue with Email
+
+
+ )}
{t("signup.create-policy")}
From 59fc34412dde33add0068a73d2e93af344e3bdbe Mon Sep 17 00:00:00 2001
From: Maidul Islam
Date: Thu, 27 Jun 2024 20:35:15 -0400
Subject: [PATCH 09/13] small nits for admin login toggle pr
---
.../src/ee/services/ldap-config/ldap-config-service.ts | 2 +-
backend/src/ee/services/oidc/oidc-config-service.ts | 2 +-
.../src/ee/services/saml-config/saml-config-service.ts | 2 +-
backend/src/services/auth/auth-login-service.ts | 8 ++++----
frontend/src/views/admin/DashboardPage/AuthPanel.tsx | 2 +-
frontend/src/views/admin/DashboardPage/DashboardPage.tsx | 4 ++--
6 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/backend/src/ee/services/ldap-config/ldap-config-service.ts b/backend/src/ee/services/ldap-config/ldap-config-service.ts
index 7434862fc7..55129ca025 100644
--- a/backend/src/ee/services/ldap-config/ldap-config-service.ts
+++ b/backend/src/ee/services/ldap-config/ldap-config-service.ts
@@ -415,7 +415,7 @@ export const ldapConfigServiceFactory = ({
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.LDAP)) {
throw new BadRequestError({
- message: "Login with LDAP is disabled."
+ message: "Login with LDAP is disabled by administrator."
});
}
diff --git a/backend/src/ee/services/oidc/oidc-config-service.ts b/backend/src/ee/services/oidc/oidc-config-service.ts
index 58b098522c..55c929b9a9 100644
--- a/backend/src/ee/services/oidc/oidc-config-service.ts
+++ b/backend/src/ee/services/oidc/oidc-config-service.ts
@@ -161,7 +161,7 @@ export const oidcConfigServiceFactory = ({
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.OIDC)) {
throw new BadRequestError({
- message: "Login with OIDC is disabled."
+ message: "Login with OIDC is disabled by administrator."
});
}
diff --git a/backend/src/ee/services/saml-config/saml-config-service.ts b/backend/src/ee/services/saml-config/saml-config-service.ts
index 286a85746d..9ac6c9c5ed 100644
--- a/backend/src/ee/services/saml-config/saml-config-service.ts
+++ b/backend/src/ee/services/saml-config/saml-config-service.ts
@@ -339,7 +339,7 @@ export const samlConfigServiceFactory = ({
if (serverCfg.enabledLoginMethods && !serverCfg.enabledLoginMethods.includes(LoginMethod.SAML)) {
throw new BadRequestError({
- message: "Login with SAML is disabled."
+ message: "Login with SAML is disabled by administrator."
});
}
diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts
index 24ae087b7a..2ce900b471 100644
--- a/backend/src/services/auth/auth-login-service.ts
+++ b/backend/src/services/auth/auth-login-service.ts
@@ -167,7 +167,7 @@ export const authLoginServiceFactory = ({
!providerAuthToken
) {
throw new BadRequestError({
- message: "Login with email is disabled."
+ message: "Login with email is disabled by administrator."
});
}
@@ -526,7 +526,7 @@ export const authLoginServiceFactory = ({
case AuthMethod.GITHUB: {
if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GITHUB)) {
throw new BadRequestError({
- message: "Login with Github is disabled.",
+ message: "Login with Github is disabled by administrator.",
name: "Oauth 2 login"
});
}
@@ -535,7 +535,7 @@ export const authLoginServiceFactory = ({
case AuthMethod.GOOGLE: {
if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GOOGLE)) {
throw new BadRequestError({
- message: "Login with Google is disabled.",
+ message: "Login with Google is disabled by administrator.",
name: "Oauth 2 login"
});
}
@@ -544,7 +544,7 @@ export const authLoginServiceFactory = ({
case AuthMethod.GITLAB: {
if (!serverCfg.enabledLoginMethods.includes(LoginMethod.GITLAB)) {
throw new BadRequestError({
- message: "Login with Gitlab is disabled.",
+ message: "Login with Gitlab is disabled by administrator.",
name: "Oauth 2 login"
});
}
diff --git a/frontend/src/views/admin/DashboardPage/AuthPanel.tsx b/frontend/src/views/admin/DashboardPage/AuthPanel.tsx
index f2fab83ec1..84e48ea0f6 100644
--- a/frontend/src/views/admin/DashboardPage/AuthPanel.tsx
+++ b/frontend/src/views/admin/DashboardPage/AuthPanel.tsx
@@ -117,7 +117,7 @@ export const AuthPanel = () => {
Login Methods
- Select the login methods available to users
+ Select the login methods you wish to allow for all users of this instance.
{
Admin Dashboard
-
Manage your Infisical instance.
+
Manage your instance level configurations.
{isUserLoading || isNotAllowed ? (
@@ -133,7 +133,7 @@ export const AdminDashboardPage = () => {
General
- Auth
+ Authentication
Rate Limit
From 04f54479cd6a968f2fd1e6e5bcc5459ddfa98fbd Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Sat, 29 Jun 2024 01:58:27 +0800
Subject: [PATCH 10/13] misc: implemented review comments
---
backend/src/server/routes/v1/admin-router.ts | 2 +-
.../super-admin/super-admin-service.ts | 37 +++++++++++++++++--
.../components/InitialStep/InitialStep.tsx | 6 ++-
3 files changed, 40 insertions(+), 5 deletions(-)
diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts
index 4557152bdb..73d6cc7e78 100644
--- a/backend/src/server/routes/v1/admin-router.ts
+++ b/backend/src/server/routes/v1/admin-router.ts
@@ -78,7 +78,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
});
},
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 };
}
});
diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts
index 41b97efa42..a51282db6f 100644
--- a/backend/src/services/super-admin/super-admin-service.ts
+++ b/backend/src/services/super-admin/super-admin-service.ts
@@ -12,7 +12,7 @@ import { AuthMethod } from "../auth/auth-type";
import { TOrgServiceFactory } from "../org/org-service";
import { TUserDALFactory } from "../user/user-dal";
import { TSuperAdminDALFactory } from "./super-admin-dal";
-import { TAdminSignUpDTO } from "./super-admin-types";
+import { LoginMethod, TAdminSignUpDTO } from "./super-admin-types";
type TSuperAdminServiceFactoryDep = {
serverCfgDAL: TSuperAdminDALFactory;
@@ -79,7 +79,38 @@ export const superAdminServiceFactory = ({
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:
+ "Admin has insufficient authentication methods for update operation to complete without getting locked out."
+ });
+ }
+ }
const updatedServerCfg = await serverCfgDAL.updateById(ADMIN_CONFIG_DB_UUID, data);
await keyStore.setItemWithExpiry(ADMIN_CONFIG_KEY, ADMIN_CONFIG_KEY_EXP, JSON.stringify(updatedServerCfg));
@@ -167,7 +198,7 @@ export const superAdminServiceFactory = ({
orgName: initialOrganizationName
});
- await updateServerCfg({ initialized: true });
+ await updateServerCfg({ initialized: true }, userInfo.user.id);
const token = await authService.generateUserTokens({
user: userInfo.user,
authMethod: AuthMethod.EMAIL,
diff --git a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
index 99e80048af..69a4188270 100644
--- a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
+++ b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
@@ -337,7 +337,11 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
>
)}
{!isLoading && loginError && }
- {config.allowSignUp ? (
+ {config.allowSignUp &&
+ (shouldDisplayLoginMethod(LoginMethod.EMAIL) ||
+ shouldDisplayLoginMethod(LoginMethod.GOOGLE) ||
+ shouldDisplayLoginMethod(LoginMethod.GITHUB) ||
+ shouldDisplayLoginMethod(LoginMethod.GITLAB)) ? (
From 5a1e43be4448fe2d1b069a9338a701414ae521c4 Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Sat, 29 Jun 2024 02:12:09 +0800
Subject: [PATCH 11/13] misc: only display recover when email login is enabled
---
.../Login/components/InitialStep/InitialStep.tsx | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
index 69a4188270..06c4df835b 100644
--- a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
+++ b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
@@ -352,13 +352,15 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
) : (
)}
-
-
-
- Forgot password? Recover your account
-
-
-
+ {shouldDisplayLoginMethod(LoginMethod.EMAIL) && (
+
+
+
+ Forgot password? Recover your account
+
+
+
+ )}
);
};
From 9e9b9a7b942dca579496cd276545c64e515f318a Mon Sep 17 00:00:00 2001
From: Maidul Islam
Date: Tue, 2 Jul 2024 10:53:36 -0400
Subject: [PATCH 12/13] update self lock out msg
---
backend/src/services/super-admin/super-admin-service.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts
index a51282db6f..70330c4ef7 100644
--- a/backend/src/services/super-admin/super-admin-service.ts
+++ b/backend/src/services/super-admin/super-admin-service.ts
@@ -106,8 +106,7 @@ export const superAdminServiceFactory = ({
)
) {
throw new BadRequestError({
- message:
- "Admin has insufficient authentication methods for update operation to complete without getting locked out."
+ message: "You must configure at least one auth method to prevent account lockout"
});
}
}
From b12de3e4f5901a3c2c6de931707bf0e9f8eb2cf4 Mon Sep 17 00:00:00 2001
From: Sheen Capadngan
Date: Wed, 3 Jul 2024 01:57:24 +0800
Subject: [PATCH 13/13] misc: removed usecallback
---
frontend/src/components/signup/InitialSignupStep.tsx | 8 ++------
.../views/Login/components/InitialStep/InitialStep.tsx | 7 ++-----
.../OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx | 8 ++------
3 files changed, 6 insertions(+), 17 deletions(-)
diff --git a/frontend/src/components/signup/InitialSignupStep.tsx b/frontend/src/components/signup/InitialSignupStep.tsx
index 8df6759132..3a7c6db045 100644
--- a/frontend/src/components/signup/InitialSignupStep.tsx
+++ b/frontend/src/components/signup/InitialSignupStep.tsx
@@ -1,4 +1,3 @@
-import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import Link from "next/link";
import { faGithub, faGitlab, faGoogle } from "@fortawesome/free-brands-svg-icons";
@@ -18,11 +17,8 @@ export default function InitialSignupStep({
const { t } = useTranslation();
const { config } = useServerConfig();
- const shouldDisplaySignupMethod = useCallback(
- (method: LoginMethod) =>
- !config.enabledLoginMethods || config.enabledLoginMethods.includes(method),
- [config]
- );
+ const shouldDisplaySignupMethod = (method: LoginMethod) =>
+ !config.enabledLoginMethods || config.enabledLoginMethods.includes(method);
return (
diff --git a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
index 06c4df835b..b2df75865b 100644
--- a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
+++ b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
@@ -62,11 +62,8 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
}
}, []);
- const shouldDisplayLoginMethod = useCallback(
- (method: LoginMethod) =>
- !config.enabledLoginMethods || config.enabledLoginMethods.includes(method),
- [config]
- );
+ const shouldDisplayLoginMethod = (method: LoginMethod) =>
+ !config.enabledLoginMethods || config.enabledLoginMethods.includes(method);
const handleLogin = async (e: FormEvent
) => {
e.preventDefault();
diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx
index b1a079922f..d344b8a28e 100644
--- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx
+++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx
@@ -1,5 +1,3 @@
-import { useCallback } from "react";
-
import { OrgPermissionActions, OrgPermissionSubjects, useServerConfig } from "@app/context";
import { withPermission } from "@app/hoc";
import { LoginMethod } from "@app/hooks/api/admin/types";
@@ -16,10 +14,8 @@ export const OrgAuthTab = withPermission(
config: { enabledLoginMethods }
} = useServerConfig();
- const shouldDisplaySection = useCallback(
- (method: LoginMethod) => !enabledLoginMethods || enabledLoginMethods.includes(method),
- [enabledLoginMethods]
- );
+ const shouldDisplaySection = (method: LoginMethod) =>
+ !enabledLoginMethods || enabledLoginMethods.includes(method);
return (