mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-28 15:29:21 +00:00
Merge pull request #1560 from Infisical/toggle-invite-org
Update UI for Org Settings Page
This commit is contained in:
@ -164,15 +164,17 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
method: "PATCH",
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
organizationId: z.string().trim(),
|
||||
isActive: z.boolean().optional(),
|
||||
url: z.string().trim().optional(),
|
||||
bindDN: z.string().trim().optional(),
|
||||
bindPass: z.string().trim().optional(),
|
||||
searchBase: z.string().trim().optional(),
|
||||
caCert: z.string().trim().optional()
|
||||
}),
|
||||
body: z
|
||||
.object({
|
||||
isActive: z.boolean(),
|
||||
url: z.string().trim(),
|
||||
bindDN: z.string().trim(),
|
||||
bindPass: z.string().trim(),
|
||||
searchBase: z.string().trim(),
|
||||
caCert: z.string().trim()
|
||||
})
|
||||
.partial()
|
||||
.merge(z.object({ organizationId: z.string() })),
|
||||
response: {
|
||||
200: LdapConfigsSchema
|
||||
}
|
||||
|
@ -180,21 +180,21 @@ export const ldapConfigServiceFactory = ({
|
||||
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
|
||||
if (bindDN) {
|
||||
if (bindDN !== undefined) {
|
||||
const { ciphertext: encryptedBindDN, iv: bindDNIV, tag: bindDNTag } = encryptSymmetric(bindDN, key);
|
||||
updateQuery.encryptedBindDN = encryptedBindDN;
|
||||
updateQuery.bindDNIV = bindDNIV;
|
||||
updateQuery.bindDNTag = bindDNTag;
|
||||
}
|
||||
|
||||
if (bindPass) {
|
||||
if (bindPass !== undefined) {
|
||||
const { ciphertext: encryptedBindPass, iv: bindPassIV, tag: bindPassTag } = encryptSymmetric(bindPass, key);
|
||||
updateQuery.encryptedBindPass = encryptedBindPass;
|
||||
updateQuery.bindPassIV = bindPassIV;
|
||||
updateQuery.bindPassTag = bindPassTag;
|
||||
}
|
||||
|
||||
if (caCert) {
|
||||
if (caCert !== undefined) {
|
||||
const { ciphertext: encryptedCACert, iv: caCertIV, tag: caCertTag } = encryptSymmetric(caCert, key);
|
||||
updateQuery.encryptedCACert = encryptedCACert;
|
||||
updateQuery.caCertIV = caCertIV;
|
||||
|
@ -172,7 +172,7 @@ export const samlConfigServiceFactory = ({
|
||||
keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
|
||||
if (entryPoint) {
|
||||
if (entryPoint !== undefined) {
|
||||
const {
|
||||
ciphertext: encryptedEntryPoint,
|
||||
iv: entryPointIV,
|
||||
@ -182,18 +182,19 @@ export const samlConfigServiceFactory = ({
|
||||
updateQuery.entryPointIV = entryPointIV;
|
||||
updateQuery.entryPointTag = entryPointTag;
|
||||
}
|
||||
if (issuer) {
|
||||
if (issuer !== undefined) {
|
||||
const { ciphertext: encryptedIssuer, iv: issuerIV, tag: issuerTag } = encryptSymmetric(issuer, key);
|
||||
updateQuery.encryptedIssuer = encryptedIssuer;
|
||||
updateQuery.issuerIV = issuerIV;
|
||||
updateQuery.issuerTag = issuerTag;
|
||||
}
|
||||
if (cert) {
|
||||
if (cert !== undefined) {
|
||||
const { ciphertext: encryptedCert, iv: certIV, tag: certTag } = encryptSymmetric(cert, key);
|
||||
updateQuery.encryptedCert = encryptedCert;
|
||||
updateQuery.certIV = certIV;
|
||||
updateQuery.certTag = certTag;
|
||||
}
|
||||
|
||||
const [ssoConfig] = await samlConfigDAL.update({ orgId }, updateQuery);
|
||||
await orgDAL.updateById(orgId, { authEnforced: false, scimEnabled: false });
|
||||
|
||||
|
@ -159,14 +159,6 @@
|
||||
"documentation/platform/ldap/general"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "LDAP",
|
||||
"pages": [
|
||||
"documentation/platform/ldap/overview",
|
||||
"documentation/platform/ldap/jumpcloud",
|
||||
"documentation/platform/ldap/general"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "SCIM",
|
||||
"pages": [
|
||||
|
@ -7,7 +7,7 @@ export const OrgSettingsPage = () => {
|
||||
|
||||
return (
|
||||
<div className="flex justify-center bg-bunker-800 text-white w-full py-6">
|
||||
<div className="max-w-7xl w-full px-6">
|
||||
<div className="max-w-4xl w-full px-6">
|
||||
<div className="mb-4">
|
||||
<p className="text-3xl font-semibold text-gray-200">{t("settings.org.title")}</p>
|
||||
</div>
|
||||
|
@ -21,10 +21,10 @@ import {
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
const LDAPFormSchema = z.object({
|
||||
url: z.string().min(1, "URL is requiredx"),
|
||||
bindDN: z.string().min(1, "Bind DN is requiredx"),
|
||||
bindPass: z.string().min(1, "Bind Pass is required"),
|
||||
searchBase: z.string().min(1, "Search Base is required"),
|
||||
url: z.string().default(""),
|
||||
bindDN: z.string().default(""),
|
||||
bindPass: z.string().default(""),
|
||||
searchBase: z.string().default(""),
|
||||
caCert: z.string().optional()
|
||||
});
|
||||
|
||||
@ -122,7 +122,7 @@ export const LDAPModal = ({
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
<ModalContent title="Add LDAP">
|
||||
<ModalContent title="Manage LDAP configuration">
|
||||
<form onSubmit={handleSubmit(onSSOModalSubmit)}>
|
||||
<Controller
|
||||
control={control}
|
||||
|
@ -9,7 +9,7 @@ import { OrgSSOSection } from "./OrgSSOSection";
|
||||
export const OrgAuthTab = withPermission(
|
||||
() => {
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-6">
|
||||
<OrgGeneralAuthSection />
|
||||
<OrgSSOSection />
|
||||
<OrgLDAPSection />
|
||||
|
@ -59,25 +59,44 @@ export const OrgGeneralAuthSection = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<h2 className="flex-1 text-xl font-semibold text-white mb-8">Settings</h2>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Sso}>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="enforce-org-auth"
|
||||
onCheckedChange={(value) => handleEnforceOrgAuthToggle(value)}
|
||||
isChecked={currentOrg?.authEnforced ?? false}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Enforce SAML SSO
|
||||
</Switch>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
<>
|
||||
{/* <div className="py-4">
|
||||
<div className="mb-2 flex justify-between">
|
||||
<h3 className="text-md text-mineshaft-100">Allow users to send invites</h3>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Sso}>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="allow-org-invites"
|
||||
onCheckedChange={(value) => handleEnforceOrgAuthToggle(value)}
|
||||
isChecked={currentOrg?.authEnforced ?? false}
|
||||
isDisabled={!isAllowed}
|
||||
/>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
<p className="text-sm text-mineshaft-300">Allow members to invite new users to this organization</p>
|
||||
</div> */}
|
||||
<div className="py-4">
|
||||
<div className="mb-2 flex justify-between">
|
||||
<h3 className="text-md text-mineshaft-100">Enforce SAML SSO</h3>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Sso}>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="enforce-org-auth"
|
||||
onCheckedChange={(value) => handleEnforceOrgAuthToggle(value)}
|
||||
isChecked={currentOrg?.authEnforced ?? false}
|
||||
isDisabled={!isAllowed}
|
||||
/>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
<p className="text-sm text-mineshaft-300">Enforce members to authenticate via SAML to access this organization</p>
|
||||
</div>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can enforce SAML SSO if you switch to Infisical's Pro plan."
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,6 +1,3 @@
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
@ -27,7 +24,7 @@ export const OrgLDAPSection = (): JSX.Element => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const { subscription } = useSubscription();
|
||||
const { createNotification } = useNotificationContext();
|
||||
const { data, isLoading } = useGetLDAPConfig(currentOrg?.id ?? "");
|
||||
const { data } = useGetLDAPConfig(currentOrg?.id ?? "");
|
||||
const { mutateAsync } = useUpdateLDAPConfig();
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"addLDAP",
|
||||
@ -88,50 +85,55 @@ export const OrgLDAPSection = (): JSX.Element => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600">
|
||||
<div className="flex items-center mb-8">
|
||||
<h2 className="text-xl font-semibold flex-1 text-white">LDAP</h2>
|
||||
{!isLoading && (
|
||||
<>
|
||||
<hr className="border-mineshaft-600" />
|
||||
<div className="py-4">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<h2 className="text-md text-mineshaft-100">LDAP</h2>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Ldap}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
onClick={addLDAPBtnClick}
|
||||
colorSchema="secondary"
|
||||
isDisabled={!isAllowed}
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
>
|
||||
Configure
|
||||
Manage
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-mineshaft-300">Manage LDAP authentication configuration</p>
|
||||
</div>
|
||||
{data && (
|
||||
<div className="mb-4">
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Ldap}>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="enable-saml-sso"
|
||||
onCheckedChange={(value) => handleLDAPToggle(value)}
|
||||
isChecked={data ? data.isActive : false}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Enable
|
||||
</Switch>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
<div className="py-4">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<h2 className="text-md text-mineshaft-100">Enable LDAP</h2>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Ldap}>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="enable-saml-sso"
|
||||
onCheckedChange={(value) => handleLDAPToggle(value)}
|
||||
isChecked={data ? data.isActive : false}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Enable
|
||||
</Switch>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
<p className="text-sm text-mineshaft-300">Allow members to authenticate into Infisical with LDAP</p>
|
||||
</div>
|
||||
)}
|
||||
<LDAPModal
|
||||
popUp={popUp}
|
||||
handlePopUpClose={handlePopUpClose}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can use LDAP authentication if you switch to Infisical's Enterprise plan."
|
||||
/>
|
||||
</div>
|
||||
<LDAPModal
|
||||
popUp={popUp}
|
||||
handlePopUpClose={handlePopUpClose}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can use LDAP authentication if you switch to Infisical's Enterprise plan."
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,6 +1,3 @@
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
@ -64,40 +61,47 @@ export const OrgScimSection = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="mb-8 flex items-center">
|
||||
<h2 className="flex-1 text-xl font-semibold text-white">SCIM</h2>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Read} a={OrgPermissionSubjects.Scim}>
|
||||
<>
|
||||
<hr className="border-mineshaft-600" />
|
||||
<div className="py-4">
|
||||
<div className="mb-2 flex justify-between items-center">
|
||||
<h2 className="text-md text-mineshaft-100">SCIM</h2>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Read} a={OrgPermissionSubjects.Scim}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
onClick={addScimTokenBtnClick}
|
||||
colorSchema="secondary"
|
||||
isDisabled={!isAllowed}
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
>
|
||||
Configure
|
||||
Manage
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
<p className="text-sm text-mineshaft-300">Manage SCIM configuration</p>
|
||||
</div>
|
||||
<div className="py-4">
|
||||
<div className="mb-2 flex justify-between items-center">
|
||||
<h2 className="text-md text-mineshaft-100">Enable SCIM</h2>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Scim}>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="enable-scim"
|
||||
onCheckedChange={(value) => {
|
||||
if (subscription?.scim) {
|
||||
handleEnableSCIMToggle(value)
|
||||
} else {
|
||||
handlePopUpOpen("upgradePlan");
|
||||
}
|
||||
}}
|
||||
isChecked={currentOrg?.scimEnabled ?? false}
|
||||
isDisabled={!isAllowed}
|
||||
/>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
<p className="text-sm text-mineshaft-300">Allow member provisioning/deprovisioning with SCIM</p>
|
||||
</div>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Scim}>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="enable-scim"
|
||||
onCheckedChange={(value) => {
|
||||
if (subscription?.scim) {
|
||||
handleEnableSCIMToggle(value)
|
||||
} else {
|
||||
handlePopUpOpen("upgradePlan");
|
||||
}
|
||||
}}
|
||||
isChecked={currentOrg?.scimEnabled ?? false}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Enable
|
||||
</Switch>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
<ScimTokenModal
|
||||
popUp={popUp}
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
@ -108,6 +112,6 @@ export const OrgScimSection = () => {
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can use SCIM Provisioning if you switch to Infisical's Enterprise plan."
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,6 +1,3 @@
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { Button, Switch, UpgradePlanModal } from "@app/components/v2";
|
||||
@ -81,49 +78,55 @@ export const OrgSSOSection = (): JSX.Element => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="mb-8 flex items-center">
|
||||
<h2 className="flex-1 text-xl font-semibold text-white">SAML</h2>
|
||||
{!isLoading && (
|
||||
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Sso}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
onClick={addSSOBtnClick}
|
||||
colorSchema="secondary"
|
||||
isDisabled={!isAllowed}
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
>
|
||||
Configure
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
</div>
|
||||
{/* {data && ( */}
|
||||
<div className="mb-4">
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Sso}>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="enable-saml-sso"
|
||||
onCheckedChange={(value) => handleSamlSSOToggle(value)}
|
||||
isChecked={data ? data.isActive : false}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Enable
|
||||
</Switch>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
<>
|
||||
<hr className="border-mineshaft-600" />
|
||||
<div className="py-4">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<h2 className="text-md text-mineshaft-100">SAML</h2>
|
||||
{!isLoading && (
|
||||
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Sso}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
onClick={addSSOBtnClick}
|
||||
colorSchema="secondary"
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Manage
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
</div>
|
||||
<SSOModal
|
||||
popUp={popUp}
|
||||
handlePopUpClose={handlePopUpClose}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can use SAML SSO if you switch to Infisical's Pro plan."
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-mineshaft-300">Manage SAML authentication configuration</p>
|
||||
</div>
|
||||
<div className="py-4">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<h2 className="text-md text-mineshaft-100">Enable SAML</h2>
|
||||
{!isLoading && (
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Sso}>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="enable-saml-sso"
|
||||
onCheckedChange={(value) => handleSamlSSOToggle(value)}
|
||||
isChecked={data ? data.isActive : false}
|
||||
isDisabled={!isAllowed}
|
||||
/>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-mineshaft-300">Allow members to authenticate into Infisical with SAML</p>
|
||||
</div>
|
||||
<SSOModal
|
||||
popUp={popUp}
|
||||
handlePopUpClose={handlePopUpClose}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can use SAML SSO if you switch to Infisical's Pro plan."
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import {
|
||||
@ -32,16 +32,16 @@ const ssoAuthProviders = [
|
||||
{ label: "Google SAML", value: AuthProvider.GOOGLE_SAML }
|
||||
];
|
||||
|
||||
const schema = yup
|
||||
const schema = z
|
||||
.object({
|
||||
authProvider: yup.string().required("SSO Type is required"),
|
||||
entryPoint: yup.string().required("IdP entrypoint is required"),
|
||||
issuer: yup.string().required("Issuer string is required"),
|
||||
cert: yup.string().required("IdP's public signing certificate is required")
|
||||
authProvider: z.string().min(1, "SSO Type is required"),
|
||||
entryPoint: z.string().default(""),
|
||||
issuer: z.string().default(""),
|
||||
cert: z.string().default("")
|
||||
})
|
||||
.required();
|
||||
|
||||
export type AddSSOFormData = yup.InferType<typeof schema>;
|
||||
export type AddSSOFormData = z.infer<typeof schema>;
|
||||
|
||||
type Props = {
|
||||
popUp: UsePopUpState<["addSSO"]>;
|
||||
@ -60,7 +60,7 @@ export const SSOModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props)
|
||||
defaultValues: {
|
||||
authProvider: AuthProvider.OKTA_SAML
|
||||
},
|
||||
resolver: yupResolver(schema)
|
||||
resolver: zodResolver(schema)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@ -173,7 +173,7 @@ export const SSOModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props)
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
<ModalContent title="Add SSO">
|
||||
<ModalContent title="Manage SAML configuration">
|
||||
<form onSubmit={handleSubmit(onSSOModalSubmit)}>
|
||||
<Controller
|
||||
control={control}
|
||||
|
@ -45,18 +45,21 @@ export const OrgDeleteSection = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<p className="mb-4 text-xl font-semibold text-mineshaft-100">Danger Zone</p>
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
colorSchema="danger"
|
||||
variant="outline_bg"
|
||||
type="submit"
|
||||
onClick={() => handlePopUpOpen("deleteOrg")}
|
||||
isDisabled={Boolean(membership && membership.role !== "admin")}
|
||||
>
|
||||
{`Delete ${currentOrg?.name}`}
|
||||
</Button>
|
||||
<>
|
||||
<hr className="border-mineshaft-600" />
|
||||
<div className="py-4">
|
||||
<p className="mb-4 text-md text-mineshaft-100">Danger Zone</p>
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
colorSchema="danger"
|
||||
variant="outline_bg"
|
||||
type="submit"
|
||||
onClick={() => handlePopUpOpen("deleteOrg")}
|
||||
isDisabled={Boolean(membership && membership.role !== "admin")}
|
||||
>
|
||||
{`Delete ${currentOrg?.name}`}
|
||||
</Button>
|
||||
</div>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteOrg.isOpen}
|
||||
title="Are you sure want to delete this organization?"
|
||||
@ -65,6 +68,6 @@ export const OrgDeleteSection = () => {
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={handleDeleteOrgSubmit}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -3,15 +3,12 @@ import { useOrgPermission } from "@app/context";
|
||||
import { OrgDeleteSection } from "../OrgDeleteSection";
|
||||
import { OrgIncidentContactsSection } from "../OrgIncidentContactsSection";
|
||||
import { OrgNameChangeSection } from "../OrgNameChangeSection";
|
||||
import { OrgSlugChangeSection } from "../OrgSlugChangeSection";
|
||||
|
||||
export const OrgGeneralTab = () => {
|
||||
const { membership } = useOrgPermission();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-6">
|
||||
<OrgNameChangeSection />
|
||||
<OrgSlugChangeSection />
|
||||
<OrgIncidentContactsSection />
|
||||
{membership && membership.role === "admin" && <OrgDeleteSection />}
|
||||
</div>
|
||||
|
@ -18,9 +18,10 @@ export const OrgIncidentContactsSection = () => {
|
||||
const { permission } = useOrgPermission();
|
||||
|
||||
return (
|
||||
<div className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600">
|
||||
<div className="flex justify-between mb-4">
|
||||
<p className="min-w-max text-xl font-semibold">{t("section.incident.incident-contacts")}</p>
|
||||
<>
|
||||
<hr className="border-mineshaft-600" />
|
||||
<div className="flex items-center justify-between pt-4">
|
||||
<p className="text-md text-mineshaft-100">{t("section.incident.incident-contacts")}</p>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.IncidentAccount}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
@ -35,16 +36,18 @@ export const OrgIncidentContactsSection = () => {
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
{permission.can(OrgPermissionActions.Read, OrgPermissionSubjects.IncidentAccount) ? (
|
||||
<OrgIncidentContactsTable />
|
||||
) : (
|
||||
<PermissionDeniedBanner />
|
||||
)}
|
||||
<div className="py-4">
|
||||
{permission.can(OrgPermissionActions.Read, OrgPermissionSubjects.IncidentAccount) ? (
|
||||
<OrgIncidentContactsTable />
|
||||
) : (
|
||||
<PermissionDeniedBanner />
|
||||
)}
|
||||
</div>
|
||||
<AddOrgIncidentContactModal
|
||||
popUp={popUp}
|
||||
handlePopUpClose={handlePopUpClose}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -10,11 +10,12 @@ import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@a
|
||||
import { useUpdateOrg } from "@app/hooks/api";
|
||||
|
||||
const formSchema = yup.object({
|
||||
name: yup
|
||||
.string()
|
||||
.required()
|
||||
.label("Organization Name")
|
||||
.max(64, "Too long, maximum length is 64 characters")
|
||||
name: yup.string().required().label("Organization Name").max(64, "Too long, maximum length is 64 characters"),
|
||||
slug: yup
|
||||
.string()
|
||||
.matches(/^[a-zA-Z0-9-]+$/, "Name must only contain alphanumeric characters or hyphens")
|
||||
.required()
|
||||
.label("Organization Slug")
|
||||
});
|
||||
|
||||
type FormData = yup.InferType<typeof formSchema>;
|
||||
@ -29,40 +30,44 @@ export const OrgNameChangeSection = (): JSX.Element => {
|
||||
|
||||
useEffect(() => {
|
||||
if (currentOrg) {
|
||||
reset({ name: currentOrg.name });
|
||||
reset({
|
||||
name: currentOrg.name,
|
||||
slug: currentOrg.slug
|
||||
});
|
||||
}
|
||||
}, [currentOrg]);
|
||||
|
||||
const onFormSubmit = async ({ name }: FormData) => {
|
||||
const onFormSubmit = async ({ name, slug }: FormData) => {
|
||||
try {
|
||||
if (!currentOrg?.id) return;
|
||||
if (name === "") return;
|
||||
|
||||
await mutateAsync({ orgId: currentOrg?.id, name });
|
||||
await mutateAsync({
|
||||
orgId: currentOrg?.id,
|
||||
name,
|
||||
slug
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully renamed organization",
|
||||
text: "Successfully updated organization details",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: "Failed to rename organization",
|
||||
text: "Failed to update organization details",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit(onFormSubmit)}
|
||||
className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
||||
>
|
||||
<p className="mb-4 text-xl font-semibold text-mineshaft-100">Organization Name</p>
|
||||
<div className="mb-2 max-w-md">
|
||||
<form onSubmit={handleSubmit(onFormSubmit)} className="py-4">
|
||||
<div className="">
|
||||
<h2 className="mb-2 text-md text-mineshaft-100">Organization Name</h2>
|
||||
<Controller
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message} className="max-w-md">
|
||||
<Input placeholder="Acme Corp" {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
@ -70,6 +75,19 @@ export const OrgNameChangeSection = (): JSX.Element => {
|
||||
name="name"
|
||||
/>
|
||||
</div>
|
||||
<div className="py-4">
|
||||
<h2 className="mb-2 text-md text-mineshaft-100">Organization Slug</h2>
|
||||
<Controller
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message} className="max-w-md">
|
||||
<Input placeholder="acme" {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
control={control}
|
||||
name="slug"
|
||||
/>
|
||||
</div>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Settings}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
@ -85,4 +103,4 @@ export const OrgNameChangeSection = (): JSX.Element => {
|
||||
</OrgPermissionCan>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
};
|
@ -1,88 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { Button, FormControl, Input } from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
import { useUpdateOrg } from "@app/hooks/api";
|
||||
|
||||
const formSchema = yup.object({
|
||||
slug: yup
|
||||
.string()
|
||||
.matches(/^[a-zA-Z0-9-]+$/, "Name must only contain alphanumeric characters or hyphens")
|
||||
.required()
|
||||
.label("Project Slug")
|
||||
});
|
||||
|
||||
type FormData = yup.InferType<typeof formSchema>;
|
||||
|
||||
export const OrgSlugChangeSection = (): JSX.Element => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const { createNotification } = useNotificationContext();
|
||||
const { handleSubmit, control, reset } = useForm<FormData>({
|
||||
resolver: yupResolver(formSchema)
|
||||
});
|
||||
const { mutateAsync, isLoading } = useUpdateOrg();
|
||||
|
||||
useEffect(() => {
|
||||
if (currentOrg) {
|
||||
reset({ slug: currentOrg.slug });
|
||||
}
|
||||
}, [currentOrg]);
|
||||
|
||||
const onFormSubmit = async ({ slug }: FormData) => {
|
||||
try {
|
||||
if (!currentOrg?.id) return;
|
||||
if (slug === "") return;
|
||||
|
||||
await mutateAsync({ orgId: currentOrg?.id, slug });
|
||||
createNotification({
|
||||
text: "Successfully updated organization slug",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: "Failed to update organization slug",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit(onFormSubmit)}
|
||||
className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
||||
>
|
||||
<p className="mb-4 text-xl font-semibold text-mineshaft-100">Organization Slug</p>
|
||||
<div className="mb-2 max-w-md">
|
||||
<Controller
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||
<Input placeholder="acme" {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
control={control}
|
||||
name="slug"
|
||||
/>
|
||||
</div>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Settings}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
isDisabled={!isAllowed}
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</form>
|
||||
);
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { OrgSlugChangeSection } from "./OrgSlugChangeSection";
|
@ -6,7 +6,7 @@ import { OrgGeneralTab } from "../OrgGeneralTab";
|
||||
|
||||
const tabs = [
|
||||
{ name: "General", key: "tab-org-general" },
|
||||
{ name: "Authentication", key: "tab-org-auth" }
|
||||
{ name: "Security", key: "tab-org-security" }
|
||||
];
|
||||
export const OrgTabGroup = () => {
|
||||
return (
|
||||
|
Reference in New Issue
Block a user