import { debounce } from "lodash"; import { observer } from "mobx-react"; import { CheckboxIcon, EmailIcon, PadlockIcon } from "outline-icons"; import { useState } from "react"; import * as React from "react"; import { useTranslation, Trans } from "react-i18next"; import { useTheme } from "styled-components"; import { TeamPreference } from "@shared/types"; import ConfirmationDialog from "~/components/ConfirmationDialog"; import Flex from "~/components/Flex"; import Heading from "~/components/Heading"; import InputSelect from "~/components/InputSelect"; import PluginIcon from "~/components/PluginIcon"; import Scene from "~/components/Scene"; import Switch from "~/components/Switch"; import Text from "~/components/Text"; import env from "~/env"; import useCurrentTeam from "~/hooks/useCurrentTeam"; import useRequest from "~/hooks/useRequest"; import useStores from "~/hooks/useStores"; import useToasts from "~/hooks/useToasts"; import isCloudHosted from "~/utils/isCloudHosted"; import DomainManagement from "./components/DomainManagement"; import SettingRow from "./components/SettingRow"; function Security() { const { auth, authenticationProviders, dialogs } = useStores(); const team = useCurrentTeam(); const { t } = useTranslation(); const { showToast } = useToasts(); const theme = useTheme(); const [data, setData] = useState({ sharing: team.sharing, documentEmbeds: team.documentEmbeds, guestSignin: team.guestSignin, defaultUserRole: team.defaultUserRole, memberCollectionCreate: team.memberCollectionCreate, inviteRequired: team.inviteRequired, }); const { data: providers, loading, request, } = useRequest(() => authenticationProviders.fetchPage({})); React.useEffect(() => { if (!providers && !loading) { request(); } }, [loading, providers, request]); const showSuccessMessage = React.useMemo( () => debounce(() => { showToast(t("Settings saved"), { type: "success", }); }, 250), [showToast, t] ); const saveData = React.useCallback( async (newData) => { try { setData(newData); await auth.updateTeam(newData); showSuccessMessage(); } catch (err) { showToast(err.message, { type: "error", }); } }, [auth, showSuccessMessage, showToast] ); const handleChange = React.useCallback( async (ev: React.ChangeEvent) => { await saveData({ ...data, [ev.target.id]: ev.target.checked }); }, [data, saveData] ); const handleDefaultRoleChange = React.useCallback( async (newDefaultRole: string) => { await saveData({ ...data, defaultUserRole: newDefaultRole }); }, [data, saveData] ); const handlePreferenceChange = React.useCallback( async (ev: React.ChangeEvent) => { const preferences = { ...team.preferences, [ev.target.id]: ev.target.checked, }; await saveData({ preferences }); }, [saveData, team.preferences] ); const handleInviteRequiredChange = React.useCallback( async (ev: React.ChangeEvent) => { const inviteRequired = ev.target.checked; const newData = { ...data, inviteRequired }; if (inviteRequired) { dialogs.openModal({ isCentered: true, title: t("Are you sure you want to require invites?"), content: ( { await saveData(newData); }} submitText={t("I’m sure")} savingText={`${t("Saving")}…`} danger > , }} /> ), }); return; } await saveData(newData); }, [data, saveData, t, dialogs, team.signinMethods] ); return ( }> {t("Security")} Settings that impact the access, security, and content of your knowledge base.

{t("Sign In")}

{authenticationProviders.orderedData // filtering unconnected, until we have ability to connect from this screen .filter((provider) => provider.isConnected) .map((provider) => ( {provider.displayName} } name={provider.name} description={t("Allow members to sign-in with {{ authProvider }}", { authProvider: provider.displayName, })} > {" "} {provider.isActive ? t("Connected") : t("Disabled")} ))} {t("Email")} } name="guestSignin" description={ env.EMAIL_ENABLED ? t("Allow members to sign-in using their email address") : t("The server must have SMTP configured to enable this setting") } border={false} >

{t("Access")}

{isCloudHosted && ( )} {!data.inviteRequired && ( )} {!data.inviteRequired && ( )}

{t("Behavior")}

); } export default observer(Security);