feat: Add disconnect button on auth config pages and form login callout banner (#11389)
* added form login callout banner * form login config page changes * form login config page changes * added redirecturl and uneditable field components * added disconnect button on auth pages * Added env variables for form login auth page * added disconnect button on auth pages * updated docs link for dform login callout doc * added condition for disconnect button * added ce changes done on ee * updated css * suggested changes in PR review * suggested changes in PR review * suggested changes in PR review * reverted gitignore files changes * reverted gitignore files changes * updated logic for saving admin settings * removed unused imports * added changes for taginput field * removed console * removed warning * removed unwanted changes
This commit is contained in:
parent
18104b9291
commit
13fe125a0c
|
|
@ -15,6 +15,10 @@ APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET=
|
|||
APPSMITH_OAUTH2_GITHUB_CLIENT_ID=
|
||||
APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET=
|
||||
|
||||
# Form Login/Signup
|
||||
APPSMITH_FORM_LOGIN_DISABLED=
|
||||
APPSMITH_SIGNUP_DISABLED=
|
||||
|
||||
# Segment
|
||||
APPSMITH_SEGMENT_KEY=
|
||||
|
||||
|
|
|
|||
|
|
@ -71,8 +71,12 @@ export const getConfigsFromEnvVars = (): INJECTED_CONFIGS => {
|
|||
enableGithubOAuth: process.env.REACT_APP_OAUTH2_GITHUB_CLIENT_ID
|
||||
? process.env.REACT_APP_OAUTH2_GITHUB_CLIENT_ID.length > 0
|
||||
: false,
|
||||
disableLoginForm: !!process.env.APPSMITH_FORM_LOGIN_DISABLED,
|
||||
disableSignup: !!process.env.APPSMITH_SIGNUP_DISABLED,
|
||||
disableLoginForm: process.env.APPSMITH_FORM_LOGIN_DISABLED
|
||||
? process.env.APPSMITH_FORM_LOGIN_DISABLED.length > 0
|
||||
: false,
|
||||
disableSignup: process.env.APPSMITH_SIGNUP_DISABLED
|
||||
? process.env.APPSMITH_SIGNUP_DISABLED.length > 0
|
||||
: false,
|
||||
segment: {
|
||||
apiKey: process.env.REACT_APP_SEGMENT_KEY || "",
|
||||
ceKey: process.env.REACT_APP_SEGMENT_CE_KEY || "",
|
||||
|
|
|
|||
|
|
@ -633,6 +633,11 @@ export const CANNOT_PULL_WITH_LOCAL_UNCOMMITTED_CHANGES = () =>
|
|||
export const CANNOT_MERGE_DUE_TO_UNCOMMITTED_CHANGES = () =>
|
||||
"Your current branch has uncommitted changes. Please commit before proceeding to merge";
|
||||
|
||||
export const DISCONNECT_SERVICE_SUBHEADER = () =>
|
||||
"Changes to this section can disrupt user authentication. Proceed with caution.";
|
||||
export const DISCONNECT_SERVICE_WARNING = () =>
|
||||
"will be removed as primary method of authentication";
|
||||
|
||||
export const DISCONNECT_EXISTING_REPOSITORIES = () =>
|
||||
"Disconnect existing Repositories";
|
||||
export const DISCONNECT_EXISTING_REPOSITORIES_INFO = () =>
|
||||
|
|
@ -1034,3 +1039,6 @@ export const CONTEXT_DELETE = () => "Delete";
|
|||
export const CONTEXT_NO_PAGE = () => "No pages";
|
||||
|
||||
export const IMAGE_LOAD_ERROR = () => "Unable to display the image";
|
||||
|
||||
export const REDIRECT_URL_TOOLTIP = () =>
|
||||
"This URL will be used while configuring your Identity Provider's Callback/Redirect URL";
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from "react";
|
|||
import {
|
||||
GOOGLE_SIGNUP_SETUP_DOC,
|
||||
GITHUB_SIGNUP_SETUP_DOC,
|
||||
SIGNUP_RESTRICTION_DOC,
|
||||
} from "constants/ThirdPartyConstants";
|
||||
import {
|
||||
SettingCategories,
|
||||
|
|
@ -28,6 +29,7 @@ const Form_Auth: AdminConfigType = {
|
|||
title: "Form Login",
|
||||
subText: "Enable your organization to sign in with Appsmith Form.",
|
||||
canSave: true,
|
||||
isConnected: false,
|
||||
settings: [
|
||||
{
|
||||
id: "APPSMITH_FORM_LOGIN_DISABLED",
|
||||
|
|
@ -37,9 +39,9 @@ const Form_Auth: AdminConfigType = {
|
|||
label: "Form Login Option",
|
||||
toggleText: (value: boolean) => {
|
||||
if (value) {
|
||||
return "Enable form login/signup";
|
||||
return "Disabled";
|
||||
} else {
|
||||
return " Disable form login/signup";
|
||||
return " Enabled";
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -51,12 +53,22 @@ const Form_Auth: AdminConfigType = {
|
|||
label: "Signup",
|
||||
toggleText: (value: boolean) => {
|
||||
if (value) {
|
||||
return "Allow invited users to signup";
|
||||
return "Allow only invited users to signup";
|
||||
} else {
|
||||
return " Allow all users to signup";
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "APPSMITH_FORM_CALLOUT_BANNER",
|
||||
category: SettingCategories.FORM_AUTH,
|
||||
subCategory: "form signup",
|
||||
controlType: SettingTypes.LINK,
|
||||
label:
|
||||
"User emails are not verified. This can lead to a breach in your application.",
|
||||
url: SIGNUP_RESTRICTION_DOC,
|
||||
calloutType: "Warning",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -66,6 +78,7 @@ const Google_Auth: AdminConfigType = {
|
|||
title: "Google Authentication",
|
||||
subText: "Enable your organization to sign in with Google (OAuth).",
|
||||
canSave: true,
|
||||
isConnected: enableGoogleOAuth,
|
||||
settings: [
|
||||
{
|
||||
id: "APPSMITH_OAUTH2_GOOGLE_READ_MORE",
|
||||
|
|
@ -110,6 +123,7 @@ const Github_Auth: AdminConfigType = {
|
|||
subText:
|
||||
"Enable your organization to sign in with Github SAML single sign-on (SSO).",
|
||||
canSave: true,
|
||||
isConnected: enableGithubOAuth,
|
||||
settings: [
|
||||
{
|
||||
id: "APPSMITH_OAUTH2_GITHUB_READ_MORE",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ export enum SettingTypes {
|
|||
GROUP = "GROUP",
|
||||
TEXT = "TEXT",
|
||||
PAGE = "PAGE",
|
||||
UNEDITABLEFIELD = "UNEDITABLEFIELD",
|
||||
ACCORDION = "ACCORDION",
|
||||
TAGINPUT = "TAGINPUT",
|
||||
}
|
||||
|
||||
export enum SettingSubtype {
|
||||
|
|
@ -44,12 +47,15 @@ export interface Setting {
|
|||
isVisible?: (values: Record<string, any>) => boolean;
|
||||
isHidden?: boolean;
|
||||
isDisabled?: (values: Record<string, any>) => boolean;
|
||||
calloutType?: "Info" | "Warning";
|
||||
advanced?: Setting[];
|
||||
}
|
||||
|
||||
export interface Category {
|
||||
title: string;
|
||||
slug: string;
|
||||
subText?: string;
|
||||
isConnected?: boolean;
|
||||
children?: Category[];
|
||||
}
|
||||
|
||||
|
|
@ -74,4 +80,5 @@ export type AdminConfigType = {
|
|||
component?: React.ElementType;
|
||||
children?: AdminConfigType[];
|
||||
canSave: boolean;
|
||||
isConnected?: boolean;
|
||||
};
|
||||
|
|
|
|||
28
app/client/src/ce/utils/adminSettingsHelpers.ts
Normal file
28
app/client/src/ce/utils/adminSettingsHelpers.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { getAppsmithConfigs } from "@appsmith/configs";
|
||||
const {
|
||||
disableLoginForm,
|
||||
enableGithubOAuth,
|
||||
enableGoogleOAuth,
|
||||
} = getAppsmithConfigs();
|
||||
|
||||
export const connectedMethods = [
|
||||
enableGoogleOAuth,
|
||||
enableGithubOAuth,
|
||||
!disableLoginForm,
|
||||
].filter(Boolean);
|
||||
|
||||
export const saveAllowed = (settings: any) => {
|
||||
if (
|
||||
connectedMethods.length >= 2 ||
|
||||
(connectedMethods.length === 1 &&
|
||||
((!("APPSMITH_FORM_LOGIN_DISABLED" in settings) && !disableLoginForm) ||
|
||||
(settings["APPSMITH_OAUTH2_GOOGLE_CLIENT_ID"] !== "" &&
|
||||
enableGoogleOAuth) ||
|
||||
(settings["APPSMITH_OAUTH2_GITHUB_CLIENT_ID"] !== "" &&
|
||||
enableGithubOAuth)))
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from "@appsmith/constants/messages";
|
||||
import { isEmail } from "utils/formhelpers";
|
||||
import { Colors } from "constants/Colors";
|
||||
|
||||
const TagInputWrapper = styled.div<{ intent?: Intent }>`
|
||||
margin-right: 8px;
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ type TagInputProps = {
|
|||
/** Intent of the tags, which defines their color */
|
||||
intent?: Intent;
|
||||
hasError?: boolean;
|
||||
customError: (values: any) => void;
|
||||
customError?: (values: any) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -89,9 +90,9 @@ function TagInputComponent(props: TagInputProps) {
|
|||
error = createMessage(INVITE_USERS_VALIDATION_EMAIL_LIST);
|
||||
}
|
||||
});
|
||||
props.customError(error);
|
||||
props.customError?.(error);
|
||||
} else {
|
||||
props.customError("");
|
||||
props.customError?.("");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ function TagInputComponent(props: TagInputProps) {
|
|||
setValues(newValues);
|
||||
props.input.onChange &&
|
||||
props.input.onChange(newValues.filter(Boolean).join(","));
|
||||
validateEmail(newValues);
|
||||
props.type === "email" && validateEmail(newValues);
|
||||
};
|
||||
|
||||
const onTagsChange = (values: React.ReactNode[]) => {
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ export type TextInputProps = CommonComponentProps & {
|
|||
trimValue?: boolean;
|
||||
$padding?: string;
|
||||
useTextArea?: boolean;
|
||||
isCopy?: boolean;
|
||||
};
|
||||
|
||||
type boxReturnType = {
|
||||
|
|
|
|||
107
app/client/src/components/ads/formFields/RedirectUrlForm.tsx
Normal file
107
app/client/src/components/ads/formFields/RedirectUrlForm.tsx
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { InjectedFormProps, reduxForm } from "redux-form";
|
||||
import { HelpIcons } from "icons/HelpIcons";
|
||||
import UneditableField from "components/ads/formFields/UneditableField";
|
||||
import styled from "styled-components";
|
||||
import copy from "copy-to-clipboard";
|
||||
import { Toaster } from "components/ads/Toast";
|
||||
import { Variant } from "components/ads/common";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import TooltipComponent from "../Tooltip";
|
||||
import { Position } from "@blueprintjs/core";
|
||||
import {
|
||||
createMessage,
|
||||
REDIRECT_URL_TOOLTIP,
|
||||
} from "@appsmith/constants/messages";
|
||||
|
||||
const HelpIcon = HelpIcons.HELP_ICON;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 24px 0;
|
||||
`;
|
||||
|
||||
export const BodyContainer = styled.div`
|
||||
width: 100%;
|
||||
padding: 0 0 16px;
|
||||
`;
|
||||
|
||||
const HeaderWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
.help-icon {
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
svg {
|
||||
border-radius: 50%;
|
||||
border: 1px solid #858282;
|
||||
padding: 1px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const HeaderSecondary = styled.h3`
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.23999999463558197px;
|
||||
text-align: left;
|
||||
`;
|
||||
|
||||
function RedirectUrlForm(
|
||||
props: InjectedFormProps & { value: string; helpText?: string },
|
||||
) {
|
||||
useEffect(() => {
|
||||
props.initialize({
|
||||
"redirect-url-form": `${window.location.origin}${props.value}`,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleCopy = (value: string) => {
|
||||
copy(value);
|
||||
Toaster.show({
|
||||
text: "Redirect URL copied to clipboard",
|
||||
variant: Variant.success,
|
||||
});
|
||||
AnalyticsUtil.logEvent("REDIRECT_URL_COPIED", { snippet: value });
|
||||
};
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<HeaderWrapper>
|
||||
<HeaderSecondary>Redirect URL</HeaderSecondary>
|
||||
<TooltipComponent
|
||||
autoFocus={false}
|
||||
content={createMessage(REDIRECT_URL_TOOLTIP)}
|
||||
hoverOpenDelay={1000}
|
||||
minWidth={"180px"}
|
||||
openOnTargetFocus={false}
|
||||
position={Position.RIGHT}
|
||||
>
|
||||
<HelpIcon
|
||||
className={"help-icon"}
|
||||
color={"#858282"}
|
||||
height={13}
|
||||
width={13}
|
||||
/>
|
||||
</TooltipComponent>
|
||||
</HeaderWrapper>
|
||||
<BodyContainer>
|
||||
<UneditableField
|
||||
disabled
|
||||
handleCopy={handleCopy}
|
||||
helperText={props.helpText}
|
||||
iscopy={"true"}
|
||||
label={"URL"}
|
||||
name={"redirect-url-form"}
|
||||
/>
|
||||
</BodyContainer>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export const RedirectUrlReduxForm = reduxForm<any, any>({
|
||||
form: "Redirect URL",
|
||||
touchOnBlur: true,
|
||||
})(RedirectUrlForm);
|
||||
71
app/client/src/components/ads/formFields/UneditableField.tsx
Normal file
71
app/client/src/components/ads/formFields/UneditableField.tsx
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import React from "react";
|
||||
import {
|
||||
Field,
|
||||
WrappedFieldMetaProps,
|
||||
WrappedFieldInputProps,
|
||||
} from "redux-form";
|
||||
import InputComponent, { InputType } from "../TextInput";
|
||||
import { Intent } from "constants/DefaultTheme";
|
||||
import { Colors } from "constants/Colors";
|
||||
import styled from "styled-components";
|
||||
import { ReactComponent as CopyIcon } from "assets/icons/menu/copy-snippet.svg";
|
||||
|
||||
const Label = styled.div`
|
||||
font-size: 14px;
|
||||
margin: 8px 0;
|
||||
color: ${Colors.CHARCOAL};
|
||||
`;
|
||||
|
||||
const InputCopyWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const renderComponent = (
|
||||
componentProps: FormTextFieldProps & {
|
||||
meta: Partial<WrappedFieldMetaProps>;
|
||||
input: Partial<WrappedFieldInputProps>;
|
||||
},
|
||||
) => {
|
||||
return (
|
||||
<>
|
||||
{componentProps.label && <Label>{componentProps.label}</Label>}
|
||||
<InputCopyWrapper>
|
||||
<InputComponent {...componentProps} {...componentProps.input} fill />
|
||||
{componentProps.iscopy === "true" && (
|
||||
<CopyIcon
|
||||
height={16}
|
||||
onClick={() =>
|
||||
componentProps.handleCopy(componentProps.input.value)
|
||||
}
|
||||
width={16}
|
||||
/>
|
||||
)}
|
||||
</InputCopyWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export type FormTextFieldProps = {
|
||||
name: string;
|
||||
type?: InputType;
|
||||
label?: string;
|
||||
intent?: Intent;
|
||||
disabled?: boolean;
|
||||
autoFocus?: boolean;
|
||||
value?: string;
|
||||
helperText?: string;
|
||||
iscopy?: string;
|
||||
handleCopy: (value: string) => void;
|
||||
};
|
||||
|
||||
function UneditableField(props: FormTextFieldProps) {
|
||||
return <Field component={renderComponent} {...props} asyncControl />;
|
||||
}
|
||||
|
||||
export default UneditableField;
|
||||
|
|
@ -15,3 +15,5 @@ export const GITHUB_SIGNUP_SETUP_DOC =
|
|||
"https://docs.appsmith.com/setup/instance-configuration/github-login";
|
||||
export const EMAIL_SETUP_DOC =
|
||||
"https://docs.appsmith.com/setup/instance-configuration/email";
|
||||
export const SIGNUP_RESTRICTION_DOC =
|
||||
"https://docs.appsmith.com/setup/instance-configuration/disable-user-signup#disable-sign-up";
|
||||
|
|
|
|||
1
app/client/src/ee/utils/adminSettingsHelpers.ts
Normal file
1
app/client/src/ee/utils/adminSettingsHelpers.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from "ce/utils/adminSettingsHelpers";
|
||||
86
app/client/src/pages/Settings/FormGroup/Accordion.tsx
Normal file
86
app/client/src/pages/Settings/FormGroup/Accordion.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Setting } from "@appsmith/pages/AdminSettings/config/types";
|
||||
import { createMessage } from "@appsmith//constants/messages";
|
||||
import { StyledLabel } from "./Common";
|
||||
import Group from "./group";
|
||||
import { Icon, IconSize } from "components/ads";
|
||||
|
||||
const AccordionWrapper = styled.div`
|
||||
margin-top: 40px;
|
||||
max-width: 634px;
|
||||
`;
|
||||
|
||||
const AccordionHeader = styled(StyledLabel)`
|
||||
text-transform: capitalize;
|
||||
margin-bottom: ${(props) => props.theme.spaces[9]}px;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const AccordionBody = styled.div`
|
||||
& .hide {
|
||||
display: none;
|
||||
}
|
||||
& .callout-link {
|
||||
> div {
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Line = styled.hr`
|
||||
display: block;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid #DFDFDF;
|
||||
margin: 0 16px;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
`;
|
||||
|
||||
type AccordionProps = {
|
||||
label?: string;
|
||||
settings?: Setting[];
|
||||
isHidden?: boolean;
|
||||
category?: string;
|
||||
subCategory?: string;
|
||||
};
|
||||
|
||||
export default function Accordion({
|
||||
category,
|
||||
label,
|
||||
settings,
|
||||
subCategory,
|
||||
}: AccordionProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<AccordionWrapper>
|
||||
{label && (
|
||||
<AccordionHeader onClick={() => setIsOpen(!isOpen)}>
|
||||
<span>{createMessage(() => label)}</span>
|
||||
<Line />
|
||||
<Icon
|
||||
name={isOpen ? "expand-less" : "expand-more"}
|
||||
size={IconSize.XXL}
|
||||
/>
|
||||
</AccordionHeader>
|
||||
)}
|
||||
{isOpen && (
|
||||
<AccordionBody>
|
||||
<Group
|
||||
category={category}
|
||||
settings={settings}
|
||||
subCategory={subCategory}
|
||||
/>
|
||||
</AccordionBody>
|
||||
)}
|
||||
</AccordionWrapper>
|
||||
);
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ const StyledIcon = styled(Icon)`
|
|||
`;
|
||||
|
||||
export const StyledFormGroup = styled.div`
|
||||
width: 634px;
|
||||
width: 40rem;
|
||||
margin-bottom: ${(props) => props.theme.spaces[7]}px;
|
||||
& span.bp3-popover-target {
|
||||
display: inline-block;
|
||||
|
|
|
|||
44
app/client/src/pages/Settings/FormGroup/TagInputField.tsx
Normal file
44
app/client/src/pages/Settings/FormGroup/TagInputField.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import React from "react";
|
||||
import {
|
||||
Field,
|
||||
WrappedFieldMetaProps,
|
||||
WrappedFieldInputProps,
|
||||
} from "redux-form";
|
||||
import TagInputComponent from "components/ads/TagInputComponent";
|
||||
import { FormGroup } from "./Common";
|
||||
import { Intent } from "constants/DefaultTheme";
|
||||
import { Setting } from "@appsmith/pages/AdminSettings/config/types";
|
||||
|
||||
const renderComponent = (
|
||||
componentProps: TagListFieldProps & {
|
||||
meta: Partial<WrappedFieldMetaProps>;
|
||||
input: Partial<WrappedFieldInputProps>;
|
||||
},
|
||||
) => {
|
||||
const setting = componentProps.setting;
|
||||
return (
|
||||
<FormGroup
|
||||
className={`t--admin-settings-tag-input t--admin-settings-${setting.name ||
|
||||
setting.id}`}
|
||||
setting={setting}
|
||||
>
|
||||
<TagInputComponent {...componentProps} />
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
type TagListFieldProps = {
|
||||
name: string;
|
||||
placeholder: string;
|
||||
type: string;
|
||||
label?: string;
|
||||
intent: Intent;
|
||||
setting: Setting;
|
||||
customError?: (err: string) => void;
|
||||
};
|
||||
|
||||
function TagInputField(props: TagListFieldProps) {
|
||||
return <Field component={renderComponent} {...props} />;
|
||||
}
|
||||
|
||||
export default TagInputField;
|
||||
|
|
@ -10,7 +10,7 @@ export default function TextInput({ setting }: SettingComponentProps) {
|
|||
setting={setting}
|
||||
>
|
||||
<FormTextField
|
||||
name={setting.name || ""}
|
||||
name={setting.name || setting.id || ""}
|
||||
placeholder={createMessage(() => setting.placeholder || "")}
|
||||
type={setting.controlSubType}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ import { SETTINGS_FORM_NAME } from "constants/forms";
|
|||
import { useSelector } from "store";
|
||||
import { createMessage } from "@appsmith/constants/messages";
|
||||
import { Callout } from "components/ads/CalloutV2";
|
||||
import { RedirectUrlReduxForm } from "components/ads/formFields/RedirectUrlForm";
|
||||
import Accordion from "./Accordion";
|
||||
import TagInputField from "./TagInputField";
|
||||
import { Classes } from "@blueprintjs/core";
|
||||
import { Colors } from "constants/Colors";
|
||||
|
||||
type GroupProps = {
|
||||
name?: string;
|
||||
|
|
@ -43,11 +48,33 @@ const GroupBody = styled.div`
|
|||
display: none;
|
||||
}
|
||||
& .callout-link {
|
||||
max-width: 634px;
|
||||
> div {
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
& .tag-input {
|
||||
.t--admin-settings-tag-input {
|
||||
label {
|
||||
+ div {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.${Classes.TAG_INPUT}, .${Classes.TAG_INPUT}.${Classes.ACTIVE} {
|
||||
border: 1.2px solid ${Colors.ALTO2};
|
||||
box-shadow: none;
|
||||
.bp3-tag {
|
||||
background: #f8f8f8;
|
||||
color: #000;
|
||||
svg:hover {
|
||||
cursor: pointer;
|
||||
path {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const formValuesSelector = getFormValues(SETTINGS_FORM_NAME);
|
||||
|
|
@ -79,7 +106,7 @@ export default function Group({
|
|||
<div
|
||||
className={setting.isHidden ? "hide" : ""}
|
||||
data-testid="admin-settings-group-text-input"
|
||||
key={setting.name}
|
||||
key={setting.name || setting.id}
|
||||
>
|
||||
<TextInput setting={setting} />
|
||||
</div>
|
||||
|
|
@ -89,7 +116,7 @@ export default function Group({
|
|||
<div
|
||||
className={setting.isHidden ? "hide" : ""}
|
||||
data-testid="admin-settings-group-toggle"
|
||||
key={setting.name}
|
||||
key={setting.name || setting.id}
|
||||
>
|
||||
<Toggle setting={setting} />
|
||||
</div>
|
||||
|
|
@ -101,13 +128,13 @@ export default function Group({
|
|||
setting.isHidden ? "hide" : "callout-link"
|
||||
} t--read-more-link`}
|
||||
data-testid="admin-settings-group-link"
|
||||
key={setting.name}
|
||||
key={setting.name || setting.id}
|
||||
>
|
||||
<Callout
|
||||
action={setting.action}
|
||||
actionLabel="READ MORE"
|
||||
title={createMessage(() => setting.label || "")}
|
||||
type="Info"
|
||||
type={setting.calloutType || "Info"}
|
||||
url={setting.url}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -117,7 +144,7 @@ export default function Group({
|
|||
<div
|
||||
className={setting.isHidden ? "hide" : ""}
|
||||
data-testid="admin-settings-group-text"
|
||||
key={setting.name}
|
||||
key={setting.name || setting.id}
|
||||
>
|
||||
<Text setting={setting} />
|
||||
</div>
|
||||
|
|
@ -127,7 +154,7 @@ export default function Group({
|
|||
<div
|
||||
className={setting.isHidden ? "hide" : ""}
|
||||
data-testid="admin-settings-group-button"
|
||||
key={setting.name}
|
||||
key={setting.name || setting.id}
|
||||
>
|
||||
<Button setting={setting} />
|
||||
</div>
|
||||
|
|
@ -137,7 +164,7 @@ export default function Group({
|
|||
<div
|
||||
className={setting.isHidden ? "hide" : ""}
|
||||
data-testid="admin-settings-group"
|
||||
key={setting.name}
|
||||
key={setting.name || setting.id}
|
||||
>
|
||||
<Group
|
||||
category={category}
|
||||
|
|
@ -147,6 +174,52 @@ export default function Group({
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
case SettingTypes.UNEDITABLEFIELD:
|
||||
return (
|
||||
<div
|
||||
className={setting.isHidden ? "hide" : ""}
|
||||
data-testid="admin-settings-redirect-url"
|
||||
key={setting.name || setting.id}
|
||||
>
|
||||
<RedirectUrlReduxForm
|
||||
helpText={setting.helpText}
|
||||
value={setting.value}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case SettingTypes.TAGINPUT:
|
||||
return (
|
||||
<div
|
||||
className={setting.isHidden ? "hide" : "tag-input"}
|
||||
data-testid="admin-settings-tag-input"
|
||||
key={setting.name || setting.id}
|
||||
>
|
||||
<TagInputField
|
||||
data-cy="t--tag-input"
|
||||
intent="success"
|
||||
label={setting.label}
|
||||
name={setting.name || setting.id}
|
||||
placeholder=""
|
||||
setting={setting}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case SettingTypes.ACCORDION:
|
||||
return (
|
||||
<div
|
||||
className={setting.isHidden ? "hide" : ""}
|
||||
data-testid="admin-settings-accordion"
|
||||
key={setting.name || setting.id}
|
||||
>
|
||||
<Accordion
|
||||
category={category}
|
||||
label={setting.label}
|
||||
settings={setting.advanced}
|
||||
subCategory={subCategory}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</GroupBody>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,21 @@ import Group from "./FormGroup/group";
|
|||
import RestartBanner from "./RestartBanner";
|
||||
import AdminConfig from "./config";
|
||||
import SaveAdminSettings from "./SaveSettings";
|
||||
import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types";
|
||||
import {
|
||||
SettingTypes,
|
||||
Setting,
|
||||
} from "@appsmith/pages/AdminSettings/config/types";
|
||||
import { DisconnectService } from "./DisconnectService";
|
||||
import {
|
||||
createMessage,
|
||||
DISCONNECT_SERVICE_SUBHEADER,
|
||||
DISCONNECT_SERVICE_WARNING,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { Toaster, Variant } from "components/ads";
|
||||
import {
|
||||
connectedMethods,
|
||||
saveAllowed,
|
||||
} from "@appsmith/utils/adminSettingsHelpers";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
flex-basis: calc(100% - ${(props) => props.theme.homePage.leftPane.width}px);
|
||||
|
|
@ -28,7 +42,9 @@ const Wrapper = styled.div`
|
|||
overflow: auto;
|
||||
`;
|
||||
|
||||
const SettingsFormWrapper = styled.div``;
|
||||
const SettingsFormWrapper = styled.div`
|
||||
max-width: 40rem;
|
||||
`;
|
||||
|
||||
export const BottomSpace = styled.div`
|
||||
height: ${(props) => props.theme.settings.footerHeight + 20}px;
|
||||
|
|
@ -79,9 +95,16 @@ export function SettingsForm(
|
|||
const isSavable = AdminConfig.savableCategories.includes(
|
||||
subCategory ?? category,
|
||||
);
|
||||
const pageTitle = getSettingLabel(
|
||||
details?.title || (subCategory ?? category),
|
||||
);
|
||||
|
||||
const onSave = () => {
|
||||
dispatch(saveSettings(props.settings));
|
||||
if (saveAllowed(props.settings)) {
|
||||
dispatch(saveSettings(props.settings));
|
||||
} else {
|
||||
saveBlocked();
|
||||
}
|
||||
};
|
||||
|
||||
const onClear = () => {
|
||||
|
|
@ -90,13 +113,6 @@ export function SettingsForm(
|
|||
if (setting && setting.controlType == SettingTypes.TOGGLE) {
|
||||
props.settingsConfig[settingName] =
|
||||
props.settingsConfig[settingName].toString() == "true";
|
||||
|
||||
if (
|
||||
typeof props.settingsConfig["APPSMITH_SIGNUP_DISABLED"] ===
|
||||
"undefined"
|
||||
) {
|
||||
props.settingsConfig["APPSMITH_SIGNUP_DISABLED"] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
props.initialize(props.settingsConfig);
|
||||
|
|
@ -111,13 +127,32 @@ export function SettingsForm(
|
|||
});
|
||||
}, []);
|
||||
|
||||
const saveBlocked = () => {
|
||||
Toaster.show({
|
||||
text: "Cannot disconnect the only connected authentication method.",
|
||||
variant: Variant.danger,
|
||||
});
|
||||
};
|
||||
|
||||
const disconnect = (currentSettings: AdminConfig) => {
|
||||
const updatedSettings: any = {};
|
||||
if (connectedMethods.length >= 2) {
|
||||
_.forEach(currentSettings, (setting: Setting) => {
|
||||
if (!setting.isHidden && setting.controlType !== SettingTypes.LINK) {
|
||||
updatedSettings[setting.id] = "";
|
||||
}
|
||||
});
|
||||
dispatch(saveSettings(updatedSettings));
|
||||
} else {
|
||||
saveBlocked();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<SettingsFormWrapper>
|
||||
<HeaderWrapper>
|
||||
<SettingsHeader>
|
||||
{getSettingLabel(details?.title || (subCategory ?? category))}
|
||||
</SettingsHeader>
|
||||
<SettingsHeader>{pageTitle}</SettingsHeader>
|
||||
{details?.subText && (
|
||||
<SettingsSubHeader>{details.subText}</SettingsSubHeader>
|
||||
)}
|
||||
|
|
@ -136,6 +171,15 @@ export function SettingsForm(
|
|||
valid={props.valid}
|
||||
/>
|
||||
)}
|
||||
{details?.isConnected && (
|
||||
<DisconnectService
|
||||
disconnect={() => disconnect(settings)}
|
||||
subHeader={createMessage(DISCONNECT_SERVICE_SUBHEADER)}
|
||||
warning={`${pageTitle} ${createMessage(
|
||||
DISCONNECT_SERVICE_WARNING,
|
||||
)}`}
|
||||
/>
|
||||
)}
|
||||
<BottomSpace />
|
||||
</SettingsFormWrapper>
|
||||
{props.showReleaseNotes && (
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ export class ConfigFactory {
|
|||
name: item.id,
|
||||
...item,
|
||||
});
|
||||
|
||||
item?.advanced?.forEach((subItem) => {
|
||||
ConfigFactory.settingsMap[subItem.id] = {
|
||||
...subItem,
|
||||
};
|
||||
});
|
||||
});
|
||||
config?.children?.forEach((child) => ConfigFactory.registerSettings(child));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ export type EventName =
|
|||
| "COMMENTS_ONBOARDING_MODAL_TRIGGERED"
|
||||
| "REPLAY_UNDO"
|
||||
| "REPLAY_REDO"
|
||||
| "REDIRECT_URL_COPIED"
|
||||
| "SNIPPET_CUSTOMIZE"
|
||||
| "SNIPPET_EXECUTE"
|
||||
| "SNIPPET_FILTER"
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ if ! test -f "$KEY_FILE" || ! test -f "$CERT_FILE"; then
|
|||
exit
|
||||
fi
|
||||
|
||||
ENV_FILE=../../.env
|
||||
ENV_FILE=../../stacks/configuration/docker.env
|
||||
if ! test -f "$ENV_FILE"; then
|
||||
echo "
|
||||
Please populate the .env at the root of the project and run again
|
||||
|
|
@ -62,7 +62,7 @@ fi
|
|||
docker rm -f wildcard-nginx || true
|
||||
|
||||
uname_out="$(uname -s)"
|
||||
vars_to_substitute="$(printf '\$%s,' $(grep -o "^APPSMITH_[A-Z0-9_]\+" ../../.env | xargs))"
|
||||
vars_to_substitute="$(printf '\$%s,' $(grep -o "^APPSMITH_[A-Z0-9_]\+" "$ENV_FILE" | xargs))"
|
||||
client_proxy_pass="${default_client_proxy}"
|
||||
network_mode="bridge"
|
||||
case "${uname_out}" in
|
||||
|
|
|
|||
|
|
@ -20,8 +20,11 @@ APPSMITH_CLOUD_SERVICES_BASE_URL="https://release-cs.appsmith.com"
|
|||
#APPSMITH_OAUTH2_GITHUB_CLIENT_ID=""
|
||||
#APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET=""
|
||||
|
||||
#APPSMITH_FORM_LOGIN_DISABLED=
|
||||
#APPSMITH_SIGNUP_DISABLED=
|
||||
|
||||
#APPSMITH_SENTRY_DSN=
|
||||
#APPSMITH_SENTRY_ENVIRONMENT=
|
||||
|
||||
#APPSMITH_RECAPTCHA_SITE_KEY=""
|
||||
#APPSMITH_RECAPTCHA_SECRET_KEY=""
|
||||
#APPSMITH_RECAPTCHA_SECRET_KEY=""
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
# APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET=""
|
||||
# APPSMITH_OAUTH2_GITHUB_CLIENT_ID=""
|
||||
# APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET=""
|
||||
# APPSMITH_FORM_LOGIN_DISABLED=
|
||||
# APPSMITH_SIGNUP_DISABLED=
|
||||
|
||||
# APPSMITH_MAIL_ENABLED=true
|
||||
# APPSMITH_MAIL_HOST=localhost
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET=
|
|||
APPSMITH_OAUTH2_GITHUB_CLIENT_ID=
|
||||
APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET=
|
||||
|
||||
# Form Login/Signup
|
||||
APPSMITH_FORM_LOGIN_DISABLED=
|
||||
APPSMITH_SIGNUP_DISABLED=
|
||||
|
||||
# Segment
|
||||
APPSMITH_SEGMENT_KEY=
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ server {
|
|||
sub_filter __APPSMITH_MAIL_ENABLED__ '\${APPSMITH_MAIL_ENABLED}';
|
||||
sub_filter __APPSMITH_DISABLE_TELEMETRY__ '\${APPSMITH_DISABLE_TELEMETRY}';
|
||||
sub_filter __APPSMITH_RECAPTCHA_SITE_KEY__ '\${APPSMITH_RECAPTCHA_SITE_KEY}';
|
||||
sub_filter __APPSMITH_FORM_LOGIN_DISABLED__ '\${APPSMITH_FORM_LOGIN_DISABLED}';
|
||||
sub_filter __APPSMITH_SIGNUP_DISABLED__ '\${APPSMITH_SIGNUP_DISABLED}';
|
||||
}
|
||||
|
||||
location /api {
|
||||
|
|
|
|||
|
|
@ -73,6 +73,8 @@ server {
|
|||
sub_filter __APPSMITH_MAIL_ENABLED__ '\${APPSMITH_MAIL_ENABLED}';
|
||||
sub_filter __APPSMITH_DISABLE_TELEMETRY__ '\${APPSMITH_DISABLE_TELEMETRY}';
|
||||
sub_filter __APPSMITH_RECAPTCHA_SITE_KEY__ '\${APPSMITH_RECAPTCHA_SITE_KEY}';
|
||||
sub_filter __APPSMITH_FORM_LOGIN_DISABLED__ '\${APPSMITH_FORM_LOGIN_DISABLED}';
|
||||
sub_filter __APPSMITH_SIGNUP_DISABLED__ '\${APPSMITH_SIGNUP_DISABLED}';
|
||||
}
|
||||
|
||||
location /api {
|
||||
|
|
|
|||
|
|
@ -198,6 +198,8 @@ To change Appsmith configurations, you can use configuration UI in application o
|
|||
| `applicationConfig.APPSMITH_ENCRYPTION_PASSWORD` | `""` |
|
||||
| `applicationConfig.APPSMITH_ENCRYPTION_SALT` | `""` |
|
||||
| `applicationConfig.APPSMITH_CUSTOM_DOMAIN` | `""` |
|
||||
| `applicationConfig.APPSMITH_FORM_LOGIN_DISABLED` | `""` |
|
||||
| `applicationConfig.APPSMITH_SIGNUP_DISABLED` | `""` |
|
||||
|
||||
For example, to change the encryption salt configuration, you can run the following command:
|
||||
```
|
||||
|
|
|
|||
|
|
@ -274,6 +274,8 @@ applicationConfig:
|
|||
APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET: ""
|
||||
APPSMITH_OAUTH2_GITHUB_CLIENT_ID: ""
|
||||
APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET: ""
|
||||
APPSMITH_FORM_LOGIN_DISABLED: ""
|
||||
APPSMITH_SIGNUP_DISABLED: ""
|
||||
APPSMITH_CLIENT_LOG_LEVEL: ""
|
||||
APPSMITH_GOOGLE_MAPS_API_KEY: ""
|
||||
APPSMITH_MAIL_ENABLED: ""
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ server {
|
|||
sub_filter __APPSMITH_RECAPTCHA_SECRET_KEY__ '${APPSMITH_RECAPTCHA_SECRET_KEY}';
|
||||
sub_filter __APPSMITH_RECAPTCHA_ENABLED__ '${APPSMITH_RECAPTCHA_ENABLED}';
|
||||
sub_filter __APPSMITH_DISABLE_INTERCOM__ '${APPSMITH_DISABLE_INTERCOM}';
|
||||
sub_filter __APPSMITH_FORM_LOGIN_DISABLED__ '${APPSMITH_FORM_LOGIN_DISABLED}';
|
||||
sub_filter __APPSMITH_SIGNUP_DISABLED__ '${APPSMITH_SIGNUP_DISABLED}';
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -34,5 +34,7 @@ data:
|
|||
APPSMITH_RECAPTCHA_SECRET_KEY: ""
|
||||
APPSMITH_RECAPTCHA_ENABLED: "false"
|
||||
APPSMITH_DISABLE_INTERCOM: "false"
|
||||
APPSMITH_FORM_LOGIN_DISABLED: "false"
|
||||
APPSMITH_SIGNUP_DISABLED: "true"
|
||||
# APPSMITH_PLUGIN_MAX_RESPONSE_SIZE_MB=5
|
||||
EOF
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ data:
|
|||
sub_filter __APPSMITH_RECAPTCHA_SECRET_KEY__ '${APPSMITH_RECAPTCHA_SECRET_KEY}';
|
||||
sub_filter __APPSMITH_RECAPTCHA_ENABLED__ '${APPSMITH_RECAPTCHA_ENABLED}';
|
||||
sub_filter __APPSMITH_DISABLE_INTERCOM__ '${APPSMITH_DISABLE_INTERCOM}';
|
||||
sub_filter __APPSMITH_FORM_LOGIN_DISABLED__ '${APPSMITH_FORM_LOGIN_DISABLED}';
|
||||
sub_filter __APPSMITH_SIGNUP_DISABLED__ '${APPSMITH_SIGNUP_DISABLED}';
|
||||
}
|
||||
|
||||
}"
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ APPSMITH_MAIL_ENABLED=false
|
|||
# APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET=
|
||||
# *********************************
|
||||
|
||||
# ******** Form Login/Signup ********
|
||||
# APPSMITH_FORM_LOGIN_DISABLED=
|
||||
# APPSMITH_SIGNUP_DISABLED=
|
||||
# ***********************************
|
||||
|
||||
# ******** Google Maps ***********
|
||||
# APPSMITH_GOOGLE_MAPS_API_KEY=
|
||||
# ********************************
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ $NGINX_SSL_CMNT server_name $custom_domain ;
|
|||
sub_filter __APPSMITH_RECAPTCHA_SECRET_KEY__ '\${APPSMITH_RECAPTCHA_SECRET_KEY}';
|
||||
sub_filter __APPSMITH_RECAPTCHA_ENABLED__ '\${APPSMITH_RECAPTCHA_ENABLED}';
|
||||
sub_filter __APPSMITH_DISABLE_INTERCOM__ '\${APPSMITH_DISABLE_INTERCOM}';
|
||||
sub_filter __APPSMITH_FORM_LOGIN_DISABLED__ '\${APPSMITH_FORM_LOGIN_DISABLED}';
|
||||
sub_filter __APPSMITH_SIGNUP_DISABLED__ '\${APPSMITH_SIGNUP_DISABLED}';
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -114,6 +116,8 @@ $NGINX_SSL_CMNT sub_filter __APPSMITH_RECAPTCHA_SITE_KEY__ '\${APPSMITH_R
|
|||
$NGINX_SSL_CMNT sub_filter __APPSMITH_RECAPTCHA_SECRET_KEY__ '\${APPSMITH_RECAPTCHA_SECRET_KEY}';
|
||||
$NGINX_SSL_CMNT sub_filter __APPSMITH_RECAPTCHA_ENABLED__ '\${APPSMITH_RECAPTCHA_ENABLED}';
|
||||
$NGINX_SSL_CMNT sub_filter __APPSMITH_DISABLE_INTERCOM__ '\${APPSMITH_DISABLE_INTERCOM}';
|
||||
$NGINX_SSL_CMNT sub_filter __APPSMITH_FORM_LOGIN_DISABLED__ '${APPSMITH_FORM_LOGIN_DISABLED}';
|
||||
$NGINX_SSL_CMNT sub_filter __APPSMITH_SIGNUP_DISABLED__ '${APPSMITH_SIGNUP_DISABLED}';
|
||||
$NGINX_SSL_CMNT }
|
||||
$NGINX_SSL_CMNT
|
||||
$NGINX_SSL_CMNT location /api {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user