0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

chore(core): guard idp-initiated sso console and api (#6690)

* chore(core): guard idp-initiated sso management api to cloud only

guard idp-initiated sso management api to cloud only

* fix(test): enable routes for integration tests

enable routes for integration tests

* feat(core, console): apply idp-initiated sso quota guard

apply idp-initiate sso quota guard
This commit is contained in:
simeng-li 2024-10-22 11:14:06 +08:00 committed by GitHub
parent c7f326edce
commit 14e65924f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 287 additions and 268 deletions

View file

@ -52,7 +52,7 @@
"access": "public"
},
"devDependencies": {
"@logto/cloud": "0.2.5-1661979",
"@logto/cloud": "0.2.5-5e334eb",
"@silverhand/eslint-config": "6.0.1",
"@silverhand/ts-config": "6.0.0",
"@types/node": "^20.11.20",

View file

@ -27,7 +27,7 @@
"devDependencies": {
"@fontsource/roboto-mono": "^5.0.0",
"@jest/types": "^29.5.0",
"@logto/cloud": "0.2.5-6654b82",
"@logto/cloud": "0.2.5-5e334eb",
"@logto/connector-kit": "workspace:^4.0.0",
"@logto/core-kit": "workspace:^2.5.0",
"@logto/elements": "workspace:^0.0.1",

View file

@ -28,6 +28,7 @@ export const skuQuotaItemPhrasesMap: Record<
customJwtEnabled: 'custom_jwt_enabled.name',
subjectTokenEnabled: 'impersonation_enabled.name',
bringYourUiEnabled: 'bring_your_ui_enabled.name',
idpInitiatedSsoEnabled: 'idp_initiated_sso_enabled.name',
};
export const skuQuotaItemUnlimitedPhrasesMap: Record<
@ -55,6 +56,7 @@ export const skuQuotaItemUnlimitedPhrasesMap: Record<
customJwtEnabled: 'custom_jwt_enabled.unlimited',
subjectTokenEnabled: 'impersonation_enabled.unlimited',
bringYourUiEnabled: 'bring_your_ui_enabled.unlimited',
idpInitiatedSsoEnabled: 'idp_initiated_sso_enabled.unlimited',
};
export const skuQuotaItemLimitedPhrasesMap: Record<
@ -82,6 +84,7 @@ export const skuQuotaItemLimitedPhrasesMap: Record<
customJwtEnabled: 'custom_jwt_enabled.limited',
subjectTokenEnabled: 'impersonation_enabled.limited',
bringYourUiEnabled: 'bring_your_ui_enabled.limited',
idpInitiatedSsoEnabled: 'idp_initiated_sso_enabled.limited',
};
export const skuQuotaItemNotEligiblePhrasesMap: Record<
@ -109,5 +112,6 @@ export const skuQuotaItemNotEligiblePhrasesMap: Record<
customJwtEnabled: 'custom_jwt_enabled.not_eligible',
subjectTokenEnabled: 'impersonation_enabled.not_eligible',
bringYourUiEnabled: 'bring_your_ui_enabled.not_eligible',
idpInitiatedSsoEnabled: 'idp_initiated_sso_enabled.not_eligible',
};
/* === for new pricing model === */

View file

@ -58,6 +58,8 @@ export const defaultLogtoSku: LogtoSkuResponse = {
updatedAt: new Date(),
type: LogtoSkuType.Basic,
unitPrice: 0,
productId: null,
defaultPriceId: null,
quota: {
// A soft limit for abuse monitoring
mauLimit: 100,
@ -80,6 +82,7 @@ export const defaultLogtoSku: LogtoSkuResponse = {
customJwtEnabled: true,
subjectTokenEnabled: true,
bringYourUiEnabled: true,
idpInitiatedSsoEnabled: false,
},
};
@ -105,6 +108,7 @@ export const defaultSubscriptionQuota: NewSubscriptionQuota = {
customJwtEnabled: false,
subjectTokenEnabled: false,
bringYourUiEnabled: false,
idpInitiatedSsoEnabled: false,
};
export const defaultSubscriptionUsage: NewSubscriptionCountBasedUsage = {
@ -125,6 +129,7 @@ export const defaultSubscriptionUsage: NewSubscriptionCountBasedUsage = {
customJwtEnabled: false,
subjectTokenEnabled: false,
bringYourUiEnabled: false,
idpInitiatedSsoEnabled: false,
};
const getAdminTenantEndpoint = () => {

View file

@ -67,12 +67,11 @@ function EnterpriseSsoDetails() {
const isDarkModeEnabled = signInExperience?.color.isDarkModeEnabled ?? false;
const isIdpInitiatedAuthEnabled = useMemo(
const isIdpInitiatedAuthConfigEnabled = useMemo(
() =>
isCloud &&
ssoConnector?.providerType === SsoProviderType.SAML &&
// TODO: @simeng: Replace this with new IdP-initiated auth quota guard
Boolean(currentSubscriptionQuota.enterpriseSsoLimit),
currentSubscriptionQuota.idpInitiatedSsoEnabled,
[ssoConnector, currentSubscriptionQuota]
);
@ -153,7 +152,7 @@ function EnterpriseSsoDetails() {
>
<DynamicT forKey="enterprise_sso_details.tab_experience" />
</TabNavItem>
{isIdpInitiatedAuthEnabled && (
{isIdpInitiatedAuthConfigEnabled && (
<TabNavItem
href={getSsoConnectorDetailsPathname(
ssoConnectorId,
@ -183,7 +182,7 @@ function EnterpriseSsoDetails() {
}}
/>
)}
{isIdpInitiatedAuthEnabled && tab === EnterpriseSsoDetailsTabs.IdpInitiatedAuth && (
{isIdpInitiatedAuthConfigEnabled && tab === EnterpriseSsoDetailsTabs.IdpInitiatedAuth && (
<IdpInitiatedAuth ssoConnector={ssoConnector} />
)}
<ConfirmModal

View file

@ -98,7 +98,7 @@
"zod": "^3.23.8"
},
"devDependencies": {
"@logto/cloud": "0.2.5-1661979",
"@logto/cloud": "0.2.5-5e334eb",
"@silverhand/eslint-config": "6.0.1",
"@silverhand/ts-config": "6.0.0",
"@types/adm-zip": "^0.5.5",

View file

@ -205,8 +205,7 @@ export default function authnRoutes<T extends AnonymousRouter>(
// All the rest of the request body will be validated and parsed by the connector.
const { RelayState: jti } = body;
// IdP initiated SSO will not have the jti in the RelayState.
// Trigger the IdP initiated SSO flow if enabled for the current connector.
// IdP initiated SSO does not provide the RelayState, we need to check if the IdP initiated SSO flow is enabled.
if (!jti && EnvSet.values.isDevFeaturesEnabled) {
const idpInitiatedAuthConfig =
await queries.ssoConnectors.getIdpInitiatedAuthConfigByConnectorId(connectorId);

View file

@ -2,6 +2,9 @@
"tags": [
{
"name": "Dev feature"
},
{
"name": "Cloud only"
}
],
"paths": {

View file

@ -10,6 +10,7 @@ import koaGuard from '#src/middleware/koa-guard.js';
import { ssoConnectorFactories } from '#src/sso/index.js';
import { tableToPathname } from '#src/utils/SchemaRouter.js';
import { koaQuotaGuard } from '../../middleware/koa-quota-guard.js';
import assertThat from '../../utils/assert-that.js';
import { type ManagementApiRouter, type RouterInitArgs } from '../types.js';
@ -24,6 +25,7 @@ export default function ssoConnectorIdpInitiatedAuthConfigRoutes<T extends Manag
queries,
libraries: {
ssoConnectors: { getSsoConnectorById, createSsoConnectorIdpInitiatedAuthConfig },
quota,
},
},
] = args;
@ -32,6 +34,7 @@ export default function ssoConnectorIdpInitiatedAuthConfigRoutes<T extends Manag
router.put(
pathPrefix,
koaQuotaGuard({ key: 'idpInitiatedSsoEnabled', quota }),
koaGuard({
body: ssoConnectorIdpInitiatedAuthConfigCreateGuard,
params: z.object({ id: z.string().min(1) }),

View file

@ -303,7 +303,10 @@ export default function singleSignOnConnectorsRoutes<T extends ManagementApiRout
}
);
if (EnvSet.values.isDevFeaturesEnabled) {
if (
EnvSet.values.isDevFeaturesEnabled &&
(EnvSet.values.isCloud || EnvSet.values.isIntegrationTest)
) {
ssoConnectorIdpInitiatedAuthConfigRoutes(...args);
}
}

View file

@ -171,6 +171,12 @@ const quota_item = {
unlimited: 'Bring your UI',
not_eligible: 'Remove your custom UI assets',
},
idp_initiated_sso_enabled: {
name: 'IDP-initiated SSO',
limited: 'IDP-initiated SSO',
unlimited: 'IDP-initiated SSO',
not_eligible: 'IDP-initiated SSO not allowed',
},
};
export default Object.freeze(quota_item);

File diff suppressed because it is too large Load diff