diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index f515fa9fff7..a8f25d7cce4 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -23829,6 +23829,7 @@ const docTemplate = `{ "browser_only", "connection_log", "custom_roles", + "external_auth", "groups", "idp_org_sync", "idp_sync", @@ -23850,6 +23851,7 @@ const docTemplate = `{ "PremiumFunnelSourceBrowserOnly", "PremiumFunnelSourceConnectionLog", "PremiumFunnelSourceCustomRoles", + "PremiumFunnelSourceExternalAuth", "PremiumFunnelSourceGroups", "PremiumFunnelSourceIdpOrgSync", "PremiumFunnelSourceIdpSync", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 6e4c74d90b0..29f75031056 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -21814,6 +21814,7 @@ "browser_only", "connection_log", "custom_roles", + "external_auth", "groups", "idp_org_sync", "idp_sync", @@ -21835,6 +21836,7 @@ "PremiumFunnelSourceBrowserOnly", "PremiumFunnelSourceConnectionLog", "PremiumFunnelSourceCustomRoles", + "PremiumFunnelSourceExternalAuth", "PremiumFunnelSourceGroups", "PremiumFunnelSourceIdpOrgSync", "PremiumFunnelSourceIdpSync", diff --git a/codersdk/premiumfunnel.go b/codersdk/premiumfunnel.go index f2901406e6a..13e8be226c3 100644 --- a/codersdk/premiumfunnel.go +++ b/codersdk/premiumfunnel.go @@ -22,6 +22,7 @@ const ( PremiumFunnelSourceBrowserOnly PremiumFunnelSource = "browser_only" PremiumFunnelSourceConnectionLog PremiumFunnelSource = "connection_log" PremiumFunnelSourceCustomRoles PremiumFunnelSource = "custom_roles" + PremiumFunnelSourceExternalAuth PremiumFunnelSource = "external_auth" PremiumFunnelSourceGroups PremiumFunnelSource = "groups" PremiumFunnelSourceIdpOrgSync PremiumFunnelSource = "idp_org_sync" PremiumFunnelSourceIdpSync PremiumFunnelSource = "idp_sync" @@ -49,6 +50,7 @@ func PremiumFunnelSources() []PremiumFunnelSource { PremiumFunnelSourceBrowserOnly, PremiumFunnelSourceConnectionLog, PremiumFunnelSourceCustomRoles, + PremiumFunnelSourceExternalAuth, PremiumFunnelSourceGroups, PremiumFunnelSourceIdpOrgSync, PremiumFunnelSourceIdpSync, diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index d8bbcbd6afe..97c7aa66928 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -10674,9 +10674,9 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith #### Enumerated Values -| Value(s) | -|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `ai_gateway_keys`, `ai_governance`, `aibridge_session_threads`, `aibridge_sessions`, `appearance`, `audit_log`, `browser_only`, `connection_log`, `custom_roles`, `direct`, `groups`, `idp_org_sync`, `idp_sync`, `multiple_organizations`, `observability`, `provisioner_keys`, `provisioners`, `template_permissions`, `workspace_proxies` | +| Value(s) | +|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ai_gateway_keys`, `ai_governance`, `aibridge_session_threads`, `aibridge_sessions`, `appearance`, `audit_log`, `browser_only`, `connection_log`, `custom_roles`, `direct`, `external_auth`, `groups`, `idp_org_sync`, `idp_sync`, `multiple_organizations`, `observability`, `provisioner_keys`, `provisioners`, `template_permissions`, `workspace_proxies` | ## codersdk.PremiumFunnelVariant diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index aa6f9279cde..2e693d2cfc3 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -7412,6 +7412,7 @@ export type PremiumFunnelSource = | "connection_log" | "custom_roles" | "direct" + | "external_auth" | "groups" | "idp_org_sync" | "idp_sync" @@ -7433,6 +7434,7 @@ export const PremiumFunnelSources: PremiumFunnelSource[] = [ "connection_log", "custom_roles", "direct", + "external_auth", "groups", "idp_org_sync", "idp_sync", diff --git a/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPage.tsx b/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPage.tsx index 6ac6a49f2ad..eb8dcab7b55 100644 --- a/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPage.tsx +++ b/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPage.tsx @@ -1,16 +1,24 @@ import type { FC } from "react"; +import { useAuthenticated } from "#/hooks/useAuthenticated"; +import { useFeatureVisibility } from "#/modules/dashboard/useFeatureVisibility"; import { useDeploymentConfig } from "#/modules/management/DeploymentConfigProvider"; import { pageTitle } from "#/utils/page"; import { ExternalAuthSettingsPageView } from "./ExternalAuthSettingsPageView"; const ExternalAuthSettingsPage: FC = () => { const { deploymentConfig } = useDeploymentConfig(); + const { permissions } = useAuthenticated(); + const { multiple_external_auth: isEntitled } = useFeatureVisibility(); return ( <> Codestin Search App - + ); }; diff --git a/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.stories.tsx b/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.stories.tsx index 339a0a59974..0b5ac04cd8c 100644 --- a/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.stories.tsx +++ b/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.stories.tsx @@ -34,6 +34,8 @@ const meta: Meta = { }, ], }, + isEntitled: false, + canViewPremium: true, }, }; @@ -44,13 +46,35 @@ export const Page: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const notice = canvas.getByRole("alert"); - await expect(within(notice).getByText("Premium")).toBeVisible(); await expect( - within(notice).getByRole("link", { - name: "Read the External Authentication documentation", - }), - ).toBeVisible(); + canvas.getByRole("link", { name: "Start trial for free" }), + ).toHaveAttribute("href", "/deployment/premium"); + }, +}; + +export const Entitled: Story = { + args: { + isEntitled: true, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + await expect( + canvas.queryByRole("link", { name: "Start trial for free" }), + ).not.toBeInTheDocument(); + }, +}; + +export const PaywallWithoutLicenseAccess: Story = { + args: { + canViewPremium: false, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + await expect( + canvas.queryByRole("link", { name: "Start trial for free" }), + ).not.toBeInTheDocument(); }, }; diff --git a/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.tsx b/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.tsx index bc67b6feee9..c27648515e2 100644 --- a/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.tsx +++ b/site/src/pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPageView.tsx @@ -3,7 +3,6 @@ import type { DeploymentValues, ExternalAuthConfig, } from "#/api/typesGenerated"; -import { Alert } from "#/components/Alert/Alert"; import { SettingsHeader, SettingsHeaderDescription, @@ -19,15 +18,19 @@ import { TableRow, } from "#/components/Table/Table"; import { TableEmpty } from "#/components/TableEmpty/TableEmpty"; +import { PremiumPaywallSmall } from "#/modules/paywall/PremiumPaywallSmall"; import { docs } from "#/utils/docs"; type ExternalAuthSettingsPageViewProps = { config: DeploymentValues; + /** True when the deployment may configure more than one provider. */ + isEntitled: boolean; + canViewPremium: boolean; }; export const ExternalAuthSettingsPageView: FC< ExternalAuthSettingsPageViewProps -> = ({ config }) => { +> = ({ config, isEntitled, canViewPremium }) => { return ( <> -
- - Integrating with multiple external authentication providers is a - Premium feature.{" "} - - Read the External Authentication documentation - - . - -
+ {!isEntitled && ( +
+ +
+ )} diff --git a/site/src/pages/DeploymentSettingsPage/SecuritySettingsPage/SecuritySettingsPageView.tsx b/site/src/pages/DeploymentSettingsPage/SecuritySettingsPage/SecuritySettingsPageView.tsx index a8e95ffd29b..82e1a64465d 100644 --- a/site/src/pages/DeploymentSettingsPage/SecuritySettingsPage/SecuritySettingsPageView.tsx +++ b/site/src/pages/DeploymentSettingsPage/SecuritySettingsPage/SecuritySettingsPageView.tsx @@ -79,8 +79,12 @@ export const SecuritySettingsPageView: FC = ({ ) : null}