Skip to content

Commit

Permalink
feat: SAML config in dashboard (#150)
Browse files Browse the repository at this point in the history
* fix: boxy changes

* fix: show form for boxy

* fix: more boxy changes

* fix: boxy stuff

* fix: remaining boxy changes

* fix: redirect url validation

* fix: boxy

* fix: version and changelog

* fix: version
  • Loading branch information
sattvikc authored Jul 24, 2024
1 parent 7a9ba91 commit 6001afb
Show file tree
Hide file tree
Showing 15 changed files with 909 additions and 19 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [0.13.0]

- Adds support for SAML metadata

## [0.12.0]

- Adds Multitenancy support to the dashboard
Expand Down
2 changes: 1 addition & 1 deletion build/static/css/main.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/static/css/main.css.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/static/js/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/static/js/bundle.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dashboard",
"version": "0.12.0",
"version": "0.13.0",
"private": true,
"dependencies": {
"@babel/core": "^7.16.0",
Expand Down
4 changes: 3 additions & 1 deletion server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ app.use(morgan("[:date[iso]] :url :method :status :response-time ms - :res[conte
SuperTokens.init({
framework: "express",
supertokens: {
connectionURI: "try.supertokens.com",
connectionURI: "localhost:3567",
},
appInfo: {
appName: "Dashboard Dev Node",
Expand Down Expand Up @@ -101,8 +101,10 @@ app.use(
})
);

// @ts-ignore
app.use(middleware());

// @ts-ignore
app.use(errorHandler());

app.get("/status", (req, res) => {
Expand Down
2 changes: 1 addition & 1 deletion src/api/tenants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export const useCreateOrUpdateThirdPartyProviderService = () => {
const createOrUpdateThirdPartyProvider = async (
tenantId: string,
providerConfig: ProviderConfig
): Promise<{ status: "OK" } | { status: "UNKNOWN_TENANT_ERROR" }> => {
): Promise<{ status: "OK" } | { status: "UNKNOWN_TENANT_ERROR" } | { status: "BOXY_ERROR"; message: string }> => {
const response = await fetchData({
url: getApiUrl("/api/thirdparty/config", tenantId),
method: "PUT",
Expand Down
2 changes: 1 addition & 1 deletion src/api/tenants/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export type ProviderClientConfig = {
clientSecret?: string;
scope?: string[] | null;
forcePKCE?: boolean;
additionalConfig?: { [key: string]: string };
additionalConfig?: { [key: string]: any };
};

export type ProviderConfig = CommonProviderConfig & {
Expand Down
1 change: 1 addition & 0 deletions src/ui/components/inputField/InputDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const InputDropdown: React.FC<InputDropdownPropTypes> = (props) => {
} ${props.size === "small" ? "input-field-small" : ""}`}
type="text"
ref={inputRef}
autoComplete="off"
/>
</div>
{isFocused && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useTenantDetailContext } from "../TenantDetailContext";
import { TenantDetailHeader } from "../TenantDetailHeader";
import { PanelHeader, PanelHeaderTitleWithTooltip, PanelRoot } from "../tenantDetailPanel/TenantDetailPanel";
import { ProviderInfoForm } from "../thirdPartyProviderConfig/ProviderInfoForm";
import { ProviderInfoFormForBoxy } from "../thirdPartyProviderConfig/ProviderInfoFormForBoxy";
import { ThirdPartyProviderInput } from "../thirdPartyProviderInput/ThirdPartyProviderInput";
import "./thirdPartyPage.scss";

Expand Down Expand Up @@ -81,12 +82,24 @@ const ProviderInfo = ({
const response = await getThirdPartyProviderInfo(tenantInfo.tenantId, id, additionalConfig);
if (response.status === "OK") {
setProviderConfigResponse(response.providerConfig);

if (
id.startsWith("boxy-saml") &&
response.providerConfig.clients![0].additionalConfig?.boxyAPIKey === undefined
) {
setHasFilledCustomFieldsForProvider(false);
}
}
setIsProviderInfoLoading(false);
};

useEffect(() => {
if (providerId !== undefined && !(isAddingNewProvider && providerHasCustomFields)) {
setHasFilledCustomFieldsForProvider(false);

if (
providerId !== undefined &&
(!(isAddingNewProvider && providerHasCustomFields) || providerId.startsWith("boxy-saml"))
) {
void fetchProviderInfo(providerId);
}
}, [providerId]);
Expand All @@ -101,6 +114,24 @@ const ProviderInfo = ({
providerId={providerId}
fetchProviderInfo={fetchProviderInfo}
handleGoBack={handleGoBack}
currentAdditionalConfig={
providerId?.startsWith("boxy-saml")
? providerConfigResponse?.clients![0].additionalConfig
: undefined
}
isAddingNewProvider={isAddingNewProvider}
/>
);
}

if (providerId?.startsWith("boxy-saml") && !hasFilledCustomFieldsForProvider) {
return (
<ProviderAdditionalConfigForm
providerId={providerId}
fetchProviderInfo={fetchProviderInfo}
handleGoBack={handleGoBack}
currentAdditionalConfig={providerConfigResponse?.clients![0].additionalConfig}
isAddingNewProvider={isAddingNewProvider}
/>
);
}
Expand All @@ -109,6 +140,17 @@ const ProviderInfo = ({
return <Loader />;
}

if (providerId?.startsWith("boxy-saml")) {
return (
<ProviderInfoFormForBoxy
providerId={providerId}
providerConfig={providerConfigResponse}
handleGoBack={handleGoBack}
isAddingNewProvider={isAddingNewProvider}
/>
);
}

return (
<ProviderInfoForm
providerId={providerId}
Expand All @@ -123,10 +165,14 @@ const ProviderAdditionalConfigForm = ({
providerId,
fetchProviderInfo,
handleGoBack,
currentAdditionalConfig,
isAddingNewProvider,
}: {
providerId: string;
fetchProviderInfo: (providerId: string, additionalConfig?: Record<string, string>) => void;
handleGoBack: () => void;
currentAdditionalConfig?: Record<string, string>;
isAddingNewProvider: boolean;
}) => {
const handleContinue = (additionalConfig: Record<string, string>) => {
fetchProviderInfo(providerId, additionalConfig);
Expand Down Expand Up @@ -160,6 +206,8 @@ const ProviderAdditionalConfigForm = ({
<BoxySamlForm
handleContinue={handleContinue}
handleGoBack={handleGoBack}
currentAdditionalConfig={currentAdditionalConfig}
isAddingNewProvider={isAddingNewProvider}
/>
);
default:
Expand All @@ -184,20 +232,37 @@ const ProviderAdditionalConfigForm = ({
type AdditionalConfigFormProps = {
handleContinue: (additionalConfig: Record<string, string>) => void;
handleGoBack: () => void;
currentAdditionalConfig?: Record<string, string>;
isAddingNewProvider?: boolean;
};

const BoxySamlForm = ({ handleContinue, handleGoBack }: AdditionalConfigFormProps) => {
const BoxySamlForm = ({
handleContinue,
handleGoBack,
currentAdditionalConfig,
isAddingNewProvider,
}: AdditionalConfigFormProps) => {
const [boxyUrl, setBoxyUrl] = useState("");
const [error, setError] = useState<string | null>(null);
const [boxyAPIKey, setBoxyAPIKey] = useState("");
const [error, setError] = useState<Record<string, string | undefined>>({});

useEffect(() => {
if (currentAdditionalConfig) {
setBoxyUrl(currentAdditionalConfig?.boxyURL ?? "");
setBoxyAPIKey(currentAdditionalConfig?.boxyAPIKey ?? "");
}
}, [currentAdditionalConfig]);

const onContinue = () => {
if (!isValidHttpUrl(boxyUrl)) {
setError("Please enter a valid URL");
setError({ boxyURL: "Please enter a valid URL" });
return;
}
handleContinue({ boxyUrl });
handleContinue({ boxyUrl, boxyAPIKey });
};

const isBoxyUrlDisabled = !isAddingNewProvider && currentAdditionalConfig?.boxyURL !== undefined;

return (
<div className="saml-intro-container">
<p className="saml-intro-container__header">
Expand All @@ -220,7 +285,7 @@ const BoxySamlForm = ({ handleContinue, handleGoBack }: AdditionalConfigFormProp
to get your Boxy URL and continue setup of your SAML client.
</SAMLInfoBox>

<p className="saml-intro-container__boxy-url-header">Add the Boxy URL below</p>
<p className="saml-intro-container__boxy-url-header">Add the Boxy Details below</p>

<div className="additional-config-field">
<ThirdPartyProviderInput
Expand All @@ -229,10 +294,27 @@ const BoxySamlForm = ({ handleContinue, handleGoBack }: AdditionalConfigFormProp
name="boxyURL"
value={boxyUrl}
forceShowError
error={error ?? undefined}
error={error.boxyURL}
disabled={isBoxyUrlDisabled}
isRequired={true}
handleChange={(e) => {
setBoxyUrl(e.target.value);
setError(null);
setError((prevError) => ({ ...prevError, boxyURL: undefined }));
}}
/>

<div style={{ height: "10px" }}></div>

<ThirdPartyProviderInput
label="Boxy API Key"
type="password"
name="boxyAPIKey"
value={boxyAPIKey}
forceShowError
error={error.boxyAPIKey}
handleChange={(e) => {
setBoxyAPIKey(e.target.value);
setError((prevError) => ({ ...prevError, boxyAPIKey: undefined }));
}}
/>
</div>
Expand Down
Loading

0 comments on commit 6001afb

Please sign in to comment.