Skip to content

Added Generic OAuth #857

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ export const IconSelect = (props: {
visible={visible}
setVisible={setVisible}
trigger="click"
leftOffset={-96}
leftOffset={-30}
searchKeywords={props.searchKeywords}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const Wrapper = styled.div`
}
`;

const IconPicker = (props: {
export const IconPicker = (props: {
value: string;
onChange: (value: string) => void;
label?: ReactNode;
Expand Down
49 changes: 26 additions & 23 deletions client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,6 @@ export function HidableView(props: {
}
}

export function ExtendedComponentView(props: {
children: JSX.Element | React.ReactNode;
className: string;
dataTestId: string;
}) {
if (!props.className && !props.dataTestId) {
return <>{props.children}</>;
}

return (
<div className={props.className} data-testid={props.dataTestId} style={{ width: "100%", height: "100%", margin: "0px", padding: "0px" }}>
{props.children}
</div>
);
}

export function ExtendedPropertyView<
ChildrenCompMap extends Record<string, Comp<unknown>>,
>(props: {
Expand Down Expand Up @@ -196,7 +180,13 @@ export class UICompBuilder<
}

override getView(): ViewReturn {
return (<div ref={this.ref} style={{height:"100%",width:"100%",margin:0,padding:0}}><UIView comp={this} viewFn={builder.viewFn} /></div>);
return (
<UIView
innerRef={this.ref}
comp={this}
viewFn={builder.viewFn}
/>
);
}

override getPropertyView(): ReactNode {
Expand All @@ -223,7 +213,11 @@ export const DisabledContext = React.createContext<boolean>(false);
/**
* Guaranteed to be in a react component, so that react hooks can be used internally
*/
function UIView(props: { comp: any; viewFn: any }) {
function UIView(props: {
innerRef: React.RefObject<HTMLDivElement>;
comp: any;
viewFn: any;
}) {
const comp = props.comp;

const childrenProps = childrenToProps(comp.children);
Expand All @@ -243,13 +237,22 @@ function UIView(props: { comp: any; viewFn: any }) {
//END ADD BY FRED

return (
<ExtendedComponentView
<div
ref={props.innerRef}
className={childrenProps.className as string}
dataTestId={childrenProps.dataTestId as string}
>
data-testid={childrenProps.dataTestId as string}
style={{
width: "100%",
height: "100%",
margin: "0px",
padding: "0px",
}}>
<HidableView hidden={childrenProps.hidden as boolean}>
{props.viewFn(childrenProps, comp.dispatch)}
{props.viewFn(
childrenProps,
comp.dispatch
)}
</HidableView>
</ExtendedComponentView>
</div>
);
}
8 changes: 6 additions & 2 deletions client/packages/lowcoder/src/constants/authConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ export const AuthRoutes: Array<{ path: string; component: React.ComponentType<an
{ path: ORG_AUTH_REGISTER_URL, component: UserRegister },
];

export type ServerAuthType = "GOOGLE" | "GITHUB" | "FORM" | "KEYCLOAK" | "ORY";
export type ServerAuthType = "GOOGLE" | "GITHUB" | "FORM" | "KEYCLOAK" | "ORY" | "GENERIC";

export type ServerAuthTypeInfoValueType = { logo: string; isOAuth2?: boolean };
export type ServerAuthTypeInfoValueType = { logo?: string; isOAuth2?: boolean };
export const ServerAuthTypeInfo: { [key in ServerAuthType]?: ServerAuthTypeInfoValueType } = {
GOOGLE: {
logo: GoogleLoginIcon,
Expand All @@ -110,6 +110,10 @@ export const ServerAuthTypeInfo: { [key in ServerAuthType]?: ServerAuthTypeInfoV
logo: OryLoginIcon,
isOAuth2: true
},
GENERIC: {
logo: undefined,
isOAuth2: true
},
FORM: { logo: EmailLoginIcon },
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { useMemo, useState } from "react";
import { messageInstance, CloseEyeIcon } from "lowcoder-design";
import { trans } from "i18n";
import {
FormStyled,
PasswordLabel
} from "../styledComponents";
import { default as Form } from "antd/es/form";
import { default as Input } from "antd/es/input";
import { default as Tooltip } from "antd/es/tooltip";
import IdSourceApi, { ConfigItem } from "api/idSourceApi";
import { validateResponse } from "api/apiUtils";
import { authConfig, AuthType, clientIdandSecretConfig, ItemType } from "../idSourceConstants";
import _ from "lodash";
import Flex from "antd/es/flex";
import Button from "antd/es/button";

type GeneralOAuthFormProp = {
authType: AuthType,
onSave: () => void;
onCancel: () => void;
};

function GeneralOAuthForm(props: GeneralOAuthFormProp) {
const {
authType,
onSave,
onCancel,
} = props;
const [form1] = Form.useForm();
const [saveLoading, setSaveLoading] = useState(false);

function saveAuthProvider(values: ConfigItem) {
setSaveLoading(true);
const config = {
...values,
authType,
enableRegister: true,
}
IdSourceApi.saveConfig(config)
.then((resp) => {
if (validateResponse(resp)) {
messageInstance.success(trans("idSource.saveSuccess"));
}
})
.catch((e) => messageInstance.error(e.message))
.finally(() => {
setSaveLoading(false);
onSave();
});
}

const handleSave = () => {
form1.validateFields().then(values => {
console.log(values);
saveAuthProvider(values);
});
}

function handleCancel() {
onCancel();
}

const authConfigForm = useMemo(() => {
if(!authConfig[authType]) return clientIdandSecretConfig;
return authConfig[authType].form;
}, [authType])

return (
<FormStyled
form={form1}
name="general"
layout="vertical"
style={{ maxWidth: '100%' }}
autoComplete="off"
>
{Object.entries(authConfigForm).map(([key, value]) => {
const valueObject = _.isObject(value) ? (value as ItemType) : false;
const required = true;
const label = valueObject ? valueObject.label : value;
const tip = valueObject && valueObject.tip;
const isPassword = valueObject && valueObject.isPassword;
return (
<div key={key}>
<Form.Item
key={key}
name={key}
rules={[
{
required,
message: trans("idSource.formPlaceholder", {
label,
}),
},
]}
label={
isPassword ? (
<PasswordLabel>
<span>{label}:</span>
<CloseEyeIcon />
</PasswordLabel>
) : (
<Tooltip title={tip}>
<span className={tip ? "has-tip" : ""}>{label}</span>:
</Tooltip>
)
}
>
{isPassword ? (
<Input
type={"password"}
placeholder={trans("idSource.encryptedServer")}
autoComplete={"one-time-code"}
/>
) : (
<Input
placeholder={trans("idSource.formPlaceholder", {
label,
})}
/>
)}
</Form.Item>
</div>
);
})}
<Flex justify="end" gap={'8px'}>
<Button
type="default"
style={{margin: 0}}
onClick={handleCancel}
>
Cancel
</Button>
<Button
type="primary"
style={{margin: 0}}
onClick={handleSave}
loading={saveLoading}
>
Save
</Button>
</Flex>
</FormStyled>
);
}

export default GeneralOAuthForm;
Loading
Loading