0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(console,core): remove devFeature guard (#5366)

remove devFeature guard for IdP
This commit is contained in:
simeng-li 2024-02-02 15:16:31 +08:00 committed by GitHub
parent 6537b7a6b5
commit c10d6b6884
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 54 additions and 158 deletions

View file

@ -2,7 +2,6 @@ import { useCallback, useMemo } from 'react';
import guides from '@/assets/docs/guides';
import { type Guide } from '@/assets/docs/guides/types';
import { isDevFeaturesEnabled } from '@/consts/env';
import {
thirdPartyAppCategory,
type AppGuideCategory,
@ -34,12 +33,7 @@ export const useAppGuideMetadata = (): {
) => Record<AppGuideCategory, readonly Guide[]>;
} => {
const appGuides = useMemo(
() =>
guides.filter(
({ metadata: { target, isThirdParty } }) =>
// @simeng-li #FIXME: remove isDevFeaturesEnabled check once we have all guides ready
target !== 'API' && (isDevFeaturesEnabled || !isThirdParty)
),
() => guides.filter(({ metadata: { target } }) => target !== 'API'),
[]
);

View file

@ -11,7 +11,7 @@ import {
ApplicationDetailsTabs,
EnterpriseSsoDetailsTabs,
} from '@/consts';
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
import { isCloud } from '@/consts/env';
import { TenantsContext } from '@/contexts/TenantsProvider';
import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
import ApiResourceDetails from '@/pages/ApiResourceDetails';
@ -82,12 +82,10 @@ function ConsoleContent() {
<Route path="dashboard" element={<Dashboard />} />
<Route path="applications">
<Route index element={<Applications />} />
{isDevFeaturesEnabled && (
<Route
path="third-party-applications"
element={<Applications tab="thirdPartyApplications" />}
/>
)}
<Route
path="third-party-applications"
element={<Applications tab="thirdPartyApplications" />}
/>
<Route path="create" element={<Applications />} />
<Route path=":id/guide/:guideId" element={<ApplicationDetails />} />
<Route path=":id">

View file

@ -11,7 +11,6 @@ import { Trans, useTranslation } from 'react-i18next';
import CaretDown from '@/assets/icons/caret-down.svg';
import CaretUp from '@/assets/icons/caret-up.svg';
import FormCard from '@/components/FormCard';
import { isDevFeaturesEnabled } from '@/consts/env';
import { openIdProviderConfigPath } from '@/consts/oidc';
import { AppDataContext } from '@/contexts/AppDataProvider';
import Button from '@/ds-components/Button';
@ -51,8 +50,8 @@ function EndpointsAndCredentials({ app: { type, secret, id, isThirdParty }, oidc
targetBlank: true,
}}
>
{/* Hide logto endpoint field in third-party application's form. @simeng-li FIXME: remove isDevFeatureEnabled flag */}
{tenantEndpoint && (!isDevFeaturesEnabled || !isThirdParty) && (
{/* Hide logto endpoint field in third-party application's form. */}
{tenantEndpoint && !isThirdParty && (
<FormField title="application_details.logto_endpoint">
<CopyToClipboard
isFullWidth

View file

@ -18,7 +18,6 @@ import DetailsPageHeader from '@/components/DetailsPage/DetailsPageHeader';
import Drawer from '@/components/Drawer';
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
import { ApplicationDetailsTabs } from '@/consts';
import { isDevFeaturesEnabled } from '@/consts/env';
import DeleteConfirmModal from '@/ds-components/DeleteConfirmModal';
import TabNav, { TabNavItem } from '@/ds-components/TabNav';
import TabWrapper from '@/ds-components/TabWrapper';
@ -165,7 +164,7 @@ function ApplicationDetailsContent({ data, oidcConfig, onApplicationUpdated }: P
</TabNavItem>
</>
)}
{isDevFeaturesEnabled && data.isThirdParty && (
{data.isThirdParty && (
<>
<TabNavItem href={`/applications/${data.id}/${ApplicationDetailsTabs.Permissions}`}>
{t('application_details.permissions.name')}
@ -218,7 +217,7 @@ function ApplicationDetailsContent({ data, oidcConfig, onApplicationUpdated }: P
</TabWrapper>
</>
)}
{isDevFeaturesEnabled && data.isThirdParty && (
{data.isThirdParty && (
<>
<TabWrapper
isActive={tab === ApplicationDetailsTabs.Permissions}

View file

@ -108,25 +108,21 @@ function GuideLibrary({ className, hasCardBorder, hasCardButton }: Props) {
<div className={styles.checkboxGroupContainer}>
<CheckboxGroup
className={styles.checkboxGroup}
options={allAppGuideCategories
.filter(
(category) => isDevFeaturesEnabled || category !== thirdPartyAppCategory
)
.map((category) => ({
title: `guide.categories.${category}`,
value: category,
...cond(
isCloud &&
category === 'ThirdParty' && {
tag: (
<FeatureTag
isVisible={currentPlan.quota.thirdPartyApplicationsLimit === 0}
plan={ReservedPlanId.Pro}
/>
),
}
),
}))}
options={allAppGuideCategories.map((category) => ({
title: `guide.categories.${category}`,
value: category,
...cond(
isCloud &&
category === 'ThirdParty' && {
tag: (
<FeatureTag
isVisible={currentPlan.quota.thirdPartyApplicationsLimit === 0}
plan={ReservedPlanId.Pro}
/>
),
}
),
}))}
value={filterCategories}
onChange={(value) => {
const sortedValue = allAppGuideCategories.filter((category) =>

View file

@ -1,60 +0,0 @@
import { type Application } from '@logto/schemas';
import { useCallback, useMemo } from 'react';
import useSWR from 'swr';
import { defaultPageSize } from '@/consts';
import { type RequestError } from '@/hooks/use-api';
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
import { buildUrl } from '@/utils/url';
const pageSize = defaultPageSize;
const applicationsEndpoint = 'api/applications';
/**
* This hook is the forked version of useApplicationsData. @see {@link (./use-application-data.ts)}
* But have all the third party application related request and code removed.
* This will be applied directly on the current application page.
* To prevent the third party api request and code from being triggered.
*
* We use the isDevFeatureEnabled to determine if we should use this legacy hook or the new one.
* This hook will be removed once we have the third-party application feature ready for production.
*/
const useLegacyApplicationsData = () => {
const [{ page }, updateSearchParameters] = useSearchParametersWatcher({
page: 1,
});
const updatePagination = useCallback(
(page: number) => {
updateSearchParameters({ page });
},
[updateSearchParameters]
);
const url = useMemo(
() =>
buildUrl(applicationsEndpoint, {
page: String(page),
page_size: String(pageSize),
}),
[page]
);
const data = useSWR<[Application[], number], RequestError>(url);
return {
...data,
pagination: {
page,
pageSize,
},
paginationRecords: {
firstPartyApplicationPage: page,
thirdPartyApplicationPage: page,
},
showThirdPartyApplicationTab: false,
updatePagination,
};
};
export default useLegacyApplicationsData;

View file

@ -8,7 +8,7 @@ import ApplicationIcon from '@/components/ApplicationIcon';
import ChargeNotification from '@/components/ChargeNotification';
import ItemPreview from '@/components/ItemPreview';
import PageMeta from '@/components/PageMeta';
import { isDevFeaturesEnabled, isCloud } from '@/consts/env';
import { isCloud } from '@/consts/env';
import Button from '@/ds-components/Button';
import CardTitle from '@/ds-components/CardTitle';
import CopyToClipboard from '@/ds-components/CopyToClipboard';
@ -23,7 +23,6 @@ import { buildUrl } from '@/utils/url';
import GuideLibrary from './components/GuideLibrary';
import GuideLibraryModal from './components/GuideLibraryModal';
import useApplicationsData from './hooks/use-application-data';
import useLegacyApplicationsData from './hooks/use-legacy-application-data';
import * as styles from './index.module.scss';
const tabs = Object.freeze({
@ -43,11 +42,6 @@ const buildTabPathWithPagePagination = (page: number, tab?: keyof typeof tabs) =
return page > 1 ? buildUrl(pathname, { page: String(page) }) : pathname;
};
// @simeng-li FIXME: Remove this when the third party applications is production ready
const useApplicationDataHook = isDevFeaturesEnabled
? useApplicationsData
: useLegacyApplicationsData;
type Props = {
tab?: keyof typeof tabs;
};
@ -68,7 +62,7 @@ function Applications({ tab }: Props) {
updatePagination,
paginationRecords,
showThirdPartyApplicationTab,
} = useApplicationDataHook(tab === 'thirdPartyApplications');
} = useApplicationsData(tab === 'thirdPartyApplications');
const isLoading = !data && !error;
const [applications, totalCount] = data ?? [];

View file

@ -4,7 +4,6 @@ import { type IRouterParamContext } from 'koa-router';
import type Provider from 'oidc-provider';
import { errors } from 'oidc-provider';
import { EnvSet } from '#src/env-set/index.js';
import { consent } from '#src/libraries/session.js';
import type Queries from '#src/tenants/Queries.js';
import assertThat from '#src/utils/assert-that.js';
@ -34,8 +33,7 @@ export default function koaAutoConsent<StateT, ContextT extends IRouterParamCont
const application =
clientId === demoAppApplicationId ? undefined : await findApplicationById(clientId);
// FIXME: @simeng-li remove this when the IdP is ready
const shouldAutoConsent = !EnvSet.values.isDevFeaturesEnabled || !application?.isThirdParty;
const shouldAutoConsent = !application?.isThirdParty;
if (shouldAutoConsent) {
const redirectTo = await consent(ctx, provider, query, interactionDetails);

View file

@ -139,8 +139,7 @@ export default function postgresAdapter(
new errors.InvalidClient(`invalid client ${id}`)
);
// FIXME: @simeng-li Remove this check after the third-party feature is released
if (EnvSet.values.isDevFeaturesEnabled && application.isThirdParty) {
if (application.isThirdParty) {
const clientScopes = await getThirdPartyClientScopes(applications, id);
return transpileClient(application, clientScopes);
}

View file

@ -33,7 +33,7 @@ import dpopValidate from 'oidc-provider/lib/helpers/validate_dpop.js';
import validatePresence from 'oidc-provider/lib/helpers/validate_presence.js';
import instance from 'oidc-provider/lib/helpers/weak_cache.js';
import { EnvSet } from '#src/env-set/index.js';
import { type EnvSet } from '#src/env-set/index.js';
import type Queries from '#src/tenants/Queries.js';
import assertThat from '#src/utils/assert-that.js';
@ -233,9 +233,7 @@ export const buildHandler: (
}
// Check if the organization is granted (third-party application only) by the user
// FIXME @simeng-li: remove the `isDevFeaturesEnabled` check when the feature is enabled
if (
EnvSet.values.isDevFeaturesEnabled &&
(await isThirdPartyApplication(queries, client.clientId)) &&
!(await isOrganizationConsentedToApplication(
queries,

View file

@ -19,7 +19,7 @@ import koaBody from 'koa-body';
import Provider, { errors } from 'oidc-provider';
import snakecaseKeys from 'snakecase-keys';
import { EnvSet } from '#src/env-set/index.js';
import { type EnvSet } from '#src/env-set/index.js';
import RequestError from '#src/errors/RequestError/index.js';
import { addOidcEventListeners } from '#src/event-listeners/index.js';
import koaAuditLog from '#src/middleware/koa-audit-log.js';
@ -129,13 +129,8 @@ export default function initOidc(envSet: EnvSet, queries: Queries, libraries: Li
const scopes = await findResourceScopes(queries, libraries, ctx, indicator);
const { client } = ctx.oidc;
// FIXME: @simeng-li Remove this check after the third-party client scope feature is released
// Need to filter out the unsupported scopes for the third-party application.
if (
EnvSet.values.isDevFeaturesEnabled &&
client &&
(await isThirdPartyApplication(queries, client.clientId))
) {
if (client && (await isThirdPartyApplication(queries, client.clientId))) {
const filteredScopes = await filterResourceScopesForTheThirdPartyApplication(
libraries,
client.clientId,

View file

@ -9,7 +9,6 @@ import { generateStandardId, generateStandardSecret } from '@logto/shared';
import { conditional } from '@silverhand/essentials';
import { boolean, object, string, z } from 'zod';
import { EnvSet } from '#src/env-set/index.js';
import RequestError from '#src/errors/RequestError/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
import koaPagination from '#src/middleware/koa-pagination.js';
@ -28,6 +27,14 @@ import {
const includesInternalAdminRole = (roles: Readonly<Array<{ role: Role }>>) =>
roles.some(({ role: { name } }) => name === InternalRole.Admin);
const parseIsThirdPartQueryParam = (isThirdPartyQuery: 'true' | 'false' | undefined) => {
if (isThirdPartyQuery === undefined) {
return;
}
return isThirdPartyQuery === 'true';
};
const applicationTypeGuard = z.nativeEnum(ApplicationType);
export default function applicationRoutes<T extends AuthedRouter>(
@ -48,27 +55,16 @@ export default function applicationRoutes<T extends AuthedRouter>(
countApplications,
findApplications,
} = queries.applications;
const {
findApplicationsRolesByApplicationId,
insertApplicationsRoles,
deleteApplicationRole,
findApplicationsRolesByRoleId,
} = queries.applicationsRoles;
const { findRoleByRoleName } = queries.roles;
const parseIsThirdPartQueryParam = (isThirdPartyQuery: 'true' | 'false ' | undefined) => {
// FIXME: @simeng-li Remove this guard once Logto as IdP is ready
if (!EnvSet.values.isDevFeaturesEnabled) {
return false;
}
if (isThirdPartyQuery === undefined) {
return;
}
return isThirdPartyQuery === 'true';
};
router.get(
'/applications',
koaPagination({ isOptional: true }),
@ -83,12 +79,7 @@ export default function applicationRoutes<T extends AuthedRouter>(
.or(applicationTypeGuard.transform((type) => [type]))
.optional(),
excludeRoleId: string().optional(),
// FIXME: @simeng-li Remove this guard once Logto as IdP is ready
...conditional(
EnvSet.values.isDevFeaturesEnabled && {
isThirdParty: z.union([z.literal('true'), z.literal('false')]).optional(),
}
),
isThirdParty: z.union([z.literal('true'), z.literal('false')]).optional(),
}),
response: z.array(applicationResponseGuard),
status: 200,
@ -98,7 +89,6 @@ export default function applicationRoutes<T extends AuthedRouter>(
const { searchParams } = ctx.URL;
const { types, excludeRoleId, isThirdParty: isThirdPartyParam } = ctx.guard.query;
// @ts-expect-error FIXME: unknown type will be fixed once we have the isDevFeaturesEnabled guard removed
const isThirdParty = parseIsThirdPartQueryParam(isThirdPartyParam);
// This will only parse the `search` query param, other params will be ignored. Please use query guard to validate them.

View file

@ -14,14 +14,13 @@ enum OriginalApplicationType {
MachineToMachine = 'MachineToMachine',
}
// FIXME: @simeng-li Remove this guard once Logto as IdP is ready
// FIXME: @wangsijie Remove this guard once protected app is ready
// @ts-expect-error -- hide the dev feature field from the guard type, but always return the full type to make the api logic simpler
export const applicationResponseGuard: typeof Applications.guard = EnvSet.values
.isDevFeaturesEnabled
? Applications.guard
: Applications.guard
.omit({ isThirdParty: true, type: true, protectedAppMetadata: true })
.omit({ type: true, protectedAppMetadata: true })
.extend({ type: z.nativeEnum(OriginalApplicationType) });
const applicationCreateGuardWithProtectedAppMetadata = originalApplicationCreateGuard
@ -65,7 +64,7 @@ export const applicationCreateGuard: typeof applicationCreateGuardWithProtectedA
.values.isDevFeaturesEnabled
? applicationCreateGuardWithProtectedAppMetadata
: applicationCreateGuardWithProtectedAppMetadata
.omit({ isThirdParty: true, type: true, protectedAppMetadata: true })
.omit({ type: true, protectedAppMetadata: true })
.extend({ type: z.nativeEnum(OriginalApplicationType) });
// FIXME: @wangsijie Remove this guard once protected app is ready

View file

@ -51,10 +51,13 @@ const createRouters = (tenant: TenantContext) => {
applicationRoutes(managementRouter, tenant);
applicationRoleRoutes(managementRouter, tenant);
// Third-party application related routes
applicationUserConsentScopeRoutes(managementRouter, tenant);
applicationSignInExperienceRoutes(managementRouter, tenant);
applicationUserConsentOrganizationRoutes(managementRouter, tenant);
// FIXME: @wangsijie: remove this after the feature is enabled by default
if (EnvSet.values.isDevFeaturesEnabled) {
applicationUserConsentScopeRoutes(managementRouter, tenant);
applicationSignInExperienceRoutes(managementRouter, tenant);
applicationUserConsentOrganizationRoutes(managementRouter, tenant);
applicationProtectedAppMetadataRoutes(managementRouter, tenant);
}

View file

@ -16,7 +16,6 @@ import { type IRouterParamContext } from 'koa-router';
import { errors } from 'oidc-provider';
import { z } from 'zod';
import { EnvSet } from '#src/env-set/index.js';
import { consent, getMissingScopes } from '#src/libraries/session.js';
import koaGuard from '#src/middleware/koa-guard.js';
import type Queries from '#src/tenants/Queries.js';
@ -125,10 +124,8 @@ export default function consentRoutes<T extends IRouterParamContext>(
},
} = ctx;
// FIXME: @simeng-li remove this when the IdP is ready
// Grant the organizations to the application if the user has selected the organizations
if (organizationIds?.length && EnvSet.values.isDevFeaturesEnabled) {
if (organizationIds?.length) {
const {
session,
params: { client_id: applicationId },
@ -163,11 +160,6 @@ export default function consentRoutes<T extends IRouterParamContext>(
}
);
// FIXME: @simeng-li remove this when the IdP is ready
if (!EnvSet.values.isDevFeaturesEnabled) {
return;
}
/**
* Get the consent info for the experience consent page.
*/

View file

@ -240,6 +240,7 @@ export default function organizationRoutes<T extends AuthedRouter>(...args: Rout
organizationRoleRoutes(...args);
organizationScopeRoutes(...args);
// FIXME: @gao-sun
if (EnvSet.values.isDevFeaturesEnabled) {
organizationInvitationRoutes(...args);
}

View file

@ -13,6 +13,7 @@ export default function systemRoutes<T extends AuthedRouter>(
},
]: RouterInitArgs<T>
) {
// FIXME: @wangsijie
if (EnvSet.values.isDevFeaturesEnabled) {
router.get(
'/systems/application',