mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 13:47:19 +08:00
Add Enterprise SSO login button to V1 login page (#13390)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -15,6 +15,7 @@ vi.mock("#/hooks/use-auth-url", () => ({
|
|||||||
bitbucket: "https://bitbucket.org/site/oauth2/authorize",
|
bitbucket: "https://bitbucket.org/site/oauth2/authorize",
|
||||||
bitbucket_data_center:
|
bitbucket_data_center:
|
||||||
"https://bitbucket-dc.example.com/site/oauth2/authorize",
|
"https://bitbucket-dc.example.com/site/oauth2/authorize",
|
||||||
|
enterprise_sso: "https://auth.example.com/realms/test/protocol/openid-connect/auth",
|
||||||
};
|
};
|
||||||
if (config.appMode === "saas") {
|
if (config.appMode === "saas") {
|
||||||
return urls[config.identityProvider] || null;
|
return urls[config.identityProvider] || null;
|
||||||
@@ -117,6 +118,74 @@ describe("LoginContent", () => {
|
|||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should display Enterprise SSO button when configured", () => {
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<LoginContent
|
||||||
|
githubAuthUrl="https://github.com/oauth/authorize"
|
||||||
|
appMode="saas"
|
||||||
|
authUrl="https://auth.example.com"
|
||||||
|
providersConfigured={["enterprise_sso"]}
|
||||||
|
/>
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByRole("button", { name: /ENTERPRISE_SSO\$CONNECT_TO_ENTERPRISE_SSO/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should display Enterprise SSO alongside other providers when all configured", () => {
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<LoginContent
|
||||||
|
githubAuthUrl="https://github.com/oauth/authorize"
|
||||||
|
appMode="saas"
|
||||||
|
authUrl="https://auth.example.com"
|
||||||
|
providersConfigured={["github", "gitlab", "bitbucket", "enterprise_sso"]}
|
||||||
|
/>
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByRole("button", { name: "GITHUB$CONNECT_TO_GITHUB" }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole("button", { name: "GITLAB$CONNECT_TO_GITLAB" }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole("button", { name: /BITBUCKET\$CONNECT_TO_BITBUCKET/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole("button", { name: /ENTERPRISE_SSO\$CONNECT_TO_ENTERPRISE_SSO/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should redirect to Enterprise SSO auth URL when Enterprise SSO button is clicked", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const mockUrl = "https://auth.example.com/realms/test/protocol/openid-connect/auth";
|
||||||
|
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<LoginContent
|
||||||
|
githubAuthUrl="https://github.com/oauth/authorize"
|
||||||
|
appMode="saas"
|
||||||
|
authUrl="https://auth.example.com"
|
||||||
|
providersConfigured={["enterprise_sso"]}
|
||||||
|
/>
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const enterpriseSsoButton = screen.getByRole("button", {
|
||||||
|
name: /ENTERPRISE_SSO\$CONNECT_TO_ENTERPRISE_SSO/i,
|
||||||
|
});
|
||||||
|
await user.click(enterpriseSsoButton);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(window.location.href).toContain(mockUrl);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("should display message when no providers are configured", () => {
|
it("should display message when no providers are configured", () => {
|
||||||
render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { FaUserShield } from "react-icons/fa";
|
||||||
import { I18nKey } from "#/i18n/declaration";
|
import { I18nKey } from "#/i18n/declaration";
|
||||||
import OpenHandsLogoWhite from "#/assets/branding/openhands-logo-white.svg?react";
|
import OpenHandsLogoWhite from "#/assets/branding/openhands-logo-white.svg?react";
|
||||||
import GitHubLogo from "#/assets/branding/github-logo.svg?react";
|
import GitHubLogo from "#/assets/branding/github-logo.svg?react";
|
||||||
@@ -65,6 +66,12 @@ export function LoginContent({
|
|||||||
authUrl,
|
authUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const enterpriseSsoAuthUrl = useAuthUrl({
|
||||||
|
appMode: appMode || null,
|
||||||
|
identityProvider: "enterprise_sso",
|
||||||
|
authUrl,
|
||||||
|
});
|
||||||
|
|
||||||
const handleAuthRedirect = async (
|
const handleAuthRedirect = async (
|
||||||
redirectUrl: string,
|
redirectUrl: string,
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
@@ -127,6 +134,12 @@ export function LoginContent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleEnterpriseSsoAuth = () => {
|
||||||
|
if (enterpriseSsoAuthUrl) {
|
||||||
|
handleAuthRedirect(enterpriseSsoAuthUrl, "enterprise_sso");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const showGithub =
|
const showGithub =
|
||||||
providersConfigured &&
|
providersConfigured &&
|
||||||
providersConfigured.length > 0 &&
|
providersConfigured.length > 0 &&
|
||||||
@@ -143,6 +156,10 @@ export function LoginContent({
|
|||||||
providersConfigured &&
|
providersConfigured &&
|
||||||
providersConfigured.length > 0 &&
|
providersConfigured.length > 0 &&
|
||||||
providersConfigured.includes("bitbucket_data_center");
|
providersConfigured.includes("bitbucket_data_center");
|
||||||
|
const showEnterpriseSso =
|
||||||
|
providersConfigured &&
|
||||||
|
providersConfigured.length > 0 &&
|
||||||
|
providersConfigured.includes("enterprise_sso");
|
||||||
|
|
||||||
const noProvidersConfigured =
|
const noProvidersConfigured =
|
||||||
!providersConfigured || providersConfigured.length === 0;
|
!providersConfigured || providersConfigured.length === 0;
|
||||||
@@ -261,6 +278,19 @@ export function LoginContent({
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showEnterpriseSso && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleEnterpriseSsoAuth}
|
||||||
|
className={`${buttonBaseClasses} bg-[#374151] text-white`}
|
||||||
|
>
|
||||||
|
<FaUserShield size={14} className="shrink-0" />
|
||||||
|
<span className={buttonLabelClasses}>
|
||||||
|
{t(I18nKey.ENTERPRISE_SSO$CONNECT_TO_ENTERPRISE_SSO)}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user