Add admin invite only signup field

This commit is contained in:
Salman
2024-02-10 03:26:56 +05:30
parent d4db01bbde
commit 6c1489a87b
9 changed files with 108 additions and 32 deletions
@@ -0,0 +1,20 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const isTablePresent = await knex.schema.hasTable(TableName.SuperAdmin);
if (isTablePresent) {
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
t.boolean("inviteOnlySignUp").defaultTo(false);
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.SuperAdmin, "inviteOnlySignUp")) {
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
t.dropColumn("inviteOnlySignUp");
});
}
}
+1
View File
@@ -11,6 +11,7 @@ export const SuperAdminSchema = z.object({
id: z.string().uuid(),
initialized: z.boolean().default(false).nullable().optional(),
allowSignUp: z.boolean().default(true).nullable().optional(),
inviteOnlySignUp: z.boolean().default(false).nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
});
+1 -1
View File
@@ -9,7 +9,7 @@ export async function seed(knex: Knex): Promise<void> {
await knex(TableName.Users).del();
await knex(TableName.UserEncryptionKey).del();
await knex(TableName.SuperAdmin).del();
await knex(TableName.SuperAdmin).insert([{ initialized: true, allowSignUp: true }]);
await knex(TableName.SuperAdmin).insert([{ initialized: true, allowSignUp: true, inviteOnlySignUp: false }]);
// Inserts seed entries
const [user] = await knex(TableName.Users)
.insert([
+1 -1
View File
@@ -105,7 +105,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
email,
firstName: profile.firstName as string,
lastName: profile.lastName as string,
isSignupAllowed: Boolean(serverCfg.allowSignUp),
isSignupAllowed: Boolean(serverCfg.allowSignUp && serverCfg.inviteOnlySignUp),
relayState: (req.body as { RelayState?: string }).RelayState,
authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string,
orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string
+2 -1
View File
@@ -31,7 +31,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
method: "PATCH",
schema: {
body: z.object({
allowSignUp: z.boolean().optional()
allowSignUp: z.boolean().optional(),
inviteOnlySignUp: z.boolean().optional()
}),
response: {
200: z.object({
@@ -33,7 +33,7 @@ export const superAdminServiceFactory = ({
const serverCfg = await serverCfgDAL.findOne({});
if (serverCfg) return;
const newCfg = await serverCfgDAL.create({ initialized: false, allowSignUp: true });
const newCfg = await serverCfgDAL.create({ initialized: false, allowSignUp: true, inviteOnlySignUp: false });
return newCfg;
};
+1
View File
@@ -1,6 +1,7 @@
export type TServerConfig = {
initialized: boolean;
allowSignUp: boolean;
inviteOnlySignUp: boolean;
isMigrationModeOn?: boolean;
};
+9 -1
View File
@@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import crypto from "crypto";
import { useState } from "react";
import { useEffect,useState } from "react";
import Head from "next/head";
import Image from "next/image";
import Link from "next/link";
@@ -22,6 +22,7 @@ import { deriveArgonKey } from "@app/components/utilities/cryptography/crypto";
import issueBackupKey from "@app/components/utilities/cryptography/issueBackupKey";
import { saveTokenToLocalStorage } from "@app/components/utilities/saveTokenToLocalStorage";
import SecurityClient from "@app/components/utilities/SecurityClient";
import { useServerConfig } from "@app/context";
import { completeAccountSignupInvite, verifySignupInvite } from "@app/hooks/api/auth/queries";
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
@@ -56,6 +57,13 @@ export default function SignupInvite() {
const token = parsedUrl.token as string;
const organizationId = parsedUrl.organization_id as string;
const email = (parsedUrl.to as string)?.replace(" ", "+").trim();
const { config } = useServerConfig();
useEffect(() => {
if (!config.allowSignUp) {
router.push("/login");
}
}, [config.allowSignUp]);
// Verifies if the information that the users entered (name, workspace) is there, and if the password matched the criteria.
const signupErrorCheck = async () => {
@@ -1,7 +1,16 @@
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import { ContentLoader, Switch, Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import {
ContentLoader,
Select,
SelectItem,
Tab,
TabList,
TabPanel,
Tabs
} from "@app/components/v2";
import { useOrganization, useServerConfig, useUser } from "@app/context";
import { useUpdateServerConfig } from "@app/hooks/api";
@@ -9,13 +18,18 @@ enum TabSections {
Settings = "settings"
}
type SignUpMode = "disabled" | "invite-only" | "anyone";
export const AdminDashboardPage = () => {
const router = useRouter();
const data = useServerConfig();
const [signUpMode, setSignUpMode] = useState<SignUpMode>("invite-only");
const { config } = data;
const { user, isLoading: isUserLoading } = useUser();
const { orgs } = useOrganization();
const { mutate: updateServerConfig } = useUpdateServerConfig();
const { createNotification } = useNotificationContext();
const isNotAllowed = !user?.superAdmin;
@@ -28,37 +42,68 @@ export const AdminDashboardPage = () => {
}
}, [isNotAllowed, isUserLoading]);
useEffect(() => {
if (!config.allowSignUp) {
setSignUpMode("disabled");
return;
}
if (config.inviteOnlySignUp) {
setSignUpMode("invite-only");
return;
}
setSignUpMode("anyone");
}, [config]);
function handleSignUpModeChange(newSignUpMode: SignUpMode) {
config.allowSignUp = newSignUpMode !== "disabled";
config.inviteOnlySignUp = newSignUpMode === "invite-only";
createNotification({
text: "Successfully changed sign up mode.",
type: "success"
});
updateServerConfig(config);
setSignUpMode(newSignUpMode);
}
return (
<div className="container mx-auto max-w-7xl pb-12 text-white dark:[color-scheme:dark]">
<div className="mx-auto mb-6 w-full max-w-7xl py-6 px-6">
<div className="container mx-auto max-w-7xl px-4 pb-12 text-white dark:[color-scheme:dark]">
<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">
<h1 className="text-3xl font-semibold">Admin Dashboard</h1>
<p className="text-base text-bunker-300">Manage your Infisical instance.</p>
</div>
{isUserLoading || isNotAllowed ? (
<ContentLoader text={isNotAllowed ? "Redirecting to org page..." : undefined} />
) : (
<div>
<Tabs defaultValue={TabSections.Settings}>
<TabList>
<div className="flex w-full flex-row border-b border-mineshaft-600">
<Tab value={TabSections.Settings}>General</Tab>
</div>
</TabList>
<TabPanel value={TabSections.Settings}>
<div className="flex items-center space-x-4">
<Switch
id="disable-invite"
isChecked={Boolean(config?.allowSignUp)}
onCheckedChange={(isChecked) => updateServerConfig({ allowSignUp: isChecked })}
/>
<div className="flex-grow">Enable signup or invite</div>
</div>
</TabPanel>
</Tabs>
</div>
)}
</div>
{isUserLoading || isNotAllowed ? (
<ContentLoader text={isNotAllowed ? "Redirecting to org page..." : undefined} />
) : (
<div>
<Tabs defaultValue={TabSections.Settings}>
<TabList>
<div className="flex w-full flex-row border-b border-mineshaft-600">
<Tab value={TabSections.Settings}>General</Tab>
</div>
</TabList>
<TabPanel value={TabSections.Settings}>
<div className="flex items-center justify-between space-x-4">
<div className="label"> Allow user to Sign Up </div>
<Select
className="w-36 bg-mineshaft-700"
dropdownContainerClassName="bg-mineshaft-700"
onValueChange={(state) => handleSignUpModeChange(state as SignUpMode)}
value={signUpMode}
isDisabled={isNotAllowed}
>
<SelectItem value="disabled">Disabled</SelectItem>
<SelectItem value="invite-only">Invite Only</SelectItem>
<SelectItem value="anyone">Anyone</SelectItem>
</Select>
</div>
</TabPanel>
</Tabs>
</div>
)}
</div>
);
};