@redocly/realm 0.130.0-next.1 → 0.130.0-next.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/dist/bin.js +1 -1
  3. package/dist/cli/develop.js +1 -1
  4. package/dist/cli/eject/resolveEjectParams.js +1 -1
  5. package/dist/cli/prepare/copy-env-files.js +1 -1
  6. package/dist/cli/stats/collectors/openapi.d.ts +3 -0
  7. package/dist/cli/stats/collectors/openapi.js +1 -0
  8. package/dist/cli/stats/index.d.ts +7 -0
  9. package/dist/cli/stats/index.js +1 -0
  10. package/dist/cli/stats/options.d.ts +3 -0
  11. package/dist/cli/stats/options.js +1 -0
  12. package/dist/cli/telemetry/index.d.ts +2 -2
  13. package/dist/cli/telemetry/index.js +1 -1
  14. package/dist/client/ErrorBoundary.js +1 -1
  15. package/dist/client/app/Sidebar/RequestAccessButton.js +2 -2
  16. package/dist/client/app/Sidebar/Sidebar.js +2 -2
  17. package/dist/client/app/hooks/catalog/useCatalogClassic.js +1 -1
  18. package/dist/client/app/hooks/catalog/useCatalogFilter.js +1 -1
  19. package/dist/client/app/hooks/catalog/useCatalogViewMode.js +1 -1
  20. package/dist/client/app/hooks/catalog/useSearchTracker.js +1 -1
  21. package/dist/client/app/hooks/usePageTimeTracker.js +1 -1
  22. package/dist/client/app/hooks/useRouteChangeTracker.js +1 -1
  23. package/dist/client/app/hooks/useTelemetry.d.ts +2 -2
  24. package/dist/client/app/pages/DevLogin/DevLogin.js +1 -1
  25. package/dist/client/app/search/useAiSearch.js +1 -1
  26. package/dist/client/app/search/useSearch.js +1 -1
  27. package/dist/client/app/telemetry/index.d.ts +11 -1
  28. package/dist/client/app/telemetry/index.js +1 -1
  29. package/dist/client/browser-entry.js +3 -3
  30. package/dist/constants/common.d.ts +3 -1
  31. package/dist/constants/common.js +1 -1
  32. package/dist/constants/l10n/langs/ar.js +1 -1
  33. package/dist/constants/l10n/langs/de.js +1 -1
  34. package/dist/constants/l10n/langs/en.js +1 -1
  35. package/dist/constants/l10n/langs/es.js +1 -1
  36. package/dist/constants/l10n/langs/fr.js +1 -1
  37. package/dist/constants/l10n/langs/hi.js +1 -1
  38. package/dist/constants/l10n/langs/it.js +1 -1
  39. package/dist/constants/l10n/langs/ja.js +1 -1
  40. package/dist/constants/l10n/langs/ko.js +1 -1
  41. package/dist/constants/l10n/langs/pl.js +1 -1
  42. package/dist/constants/l10n/langs/pt-BR.js +1 -1
  43. package/dist/constants/l10n/langs/pt.js +1 -1
  44. package/dist/constants/l10n/langs/ru.js +1 -1
  45. package/dist/constants/l10n/langs/uk.js +1 -1
  46. package/dist/constants/l10n/langs/zh.js +1 -1
  47. package/dist/server/api-routes/execute-api-route.js +1 -1
  48. package/dist/server/api-routes/run-api-routes-worker.js +1 -1
  49. package/dist/server/constants/plugins/catalog-entities.d.ts +1 -0
  50. package/dist/server/constants/plugins/catalog-entities.js +1 -1
  51. package/dist/server/esbuild/esbuild.js +3 -3
  52. package/dist/server/fs/cache.js +1 -1
  53. package/dist/server/node-bundle-entry.js +1 -1
  54. package/dist/server/persistence/kv/repositories/kv-remote-repository.d.ts +1 -0
  55. package/dist/server/persistence/kv/repositories/kv-remote-repository.js +2 -1
  56. package/dist/server/persistence/kv/services/kv-service.js +1 -1
  57. package/dist/server/plugins/asyncapi-docs/store-definition-bundles.js +1 -1
  58. package/dist/server/plugins/asyncapi-docs/template/AsyncApiDocs.js +5 -3
  59. package/dist/server/plugins/catalog-entities/database/catalog-entities-service.d.ts +11 -12
  60. package/dist/server/plugins/catalog-entities/database/catalog-entities-service.js +1 -1
  61. package/dist/server/plugins/catalog-entities/database/mappers/create-entity-attributes-db-record.d.ts +8 -0
  62. package/dist/server/plugins/catalog-entities/database/mappers/create-entity-attributes-db-record.js +1 -0
  63. package/dist/server/plugins/catalog-entities/database/mappers/create-entity-db-record.d.ts +1 -1
  64. package/dist/server/plugins/catalog-entities/database/mappers/create-entity-read-model.js +1 -1
  65. package/dist/server/plugins/catalog-entities/database/mappers/map-entity-relation-row.js +1 -1
  66. package/dist/server/plugins/catalog-entities/database/repositories/common/revision-repository.d.ts +27 -0
  67. package/dist/server/plugins/catalog-entities/database/repositories/common/revision-repository.js +1 -0
  68. package/dist/server/plugins/catalog-entities/database/repositories/common/version-repository.d.ts +36 -0
  69. package/dist/server/plugins/catalog-entities/database/repositories/common/version-repository.js +1 -0
  70. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-bff-repository.d.ts +2 -2
  71. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-bff-repository.js +34 -27
  72. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-read-repository.d.ts +5 -4
  73. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-read-repository.js +27 -14
  74. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-repository.d.ts +10 -8
  75. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-repository.js +1 -1
  76. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-write-repository.d.ts +3 -1
  77. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-write-repository.js +1 -1
  78. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-relations-repository.js +1 -1
  79. package/dist/server/plugins/catalog-entities/database/repositories/remote/catalog-entities-remote-repository.d.ts +2 -5
  80. package/dist/server/plugins/catalog-entities/database/repositories/remote/catalog-entities-remote-repository.js +1 -1
  81. package/dist/server/plugins/catalog-entities/database/repositories/utils/create-merged-entity-fields-for-select.d.ts +34 -0
  82. package/dist/server/plugins/catalog-entities/database/repositories/utils/create-merged-entity-fields-for-select.js +13 -0
  83. package/dist/server/plugins/catalog-entities/database/repositories/utils/normalize-revision-flags.d.ts +23 -0
  84. package/dist/server/plugins/catalog-entities/database/repositories/utils/normalize-revision-flags.js +1 -0
  85. package/dist/server/plugins/catalog-entities/database/repositories/utils/semantic-version-sort.d.ts +78 -0
  86. package/dist/server/plugins/catalog-entities/database/repositories/utils/semantic-version-sort.js +34 -0
  87. package/dist/server/plugins/catalog-entities/entities/validate-entity.d.ts +3 -1
  88. package/dist/server/plugins/catalog-entities/entities/validate-entity.js +1 -1
  89. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/arazzo-entities-extractor.js +1 -1
  90. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/asyncapi-entities-extractor.js +1 -1
  91. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/base.d.ts +4 -3
  92. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/base.js +1 -1
  93. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/graphql-entities-extractor.js +2 -2
  94. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/openapi-entities-extractor.d.ts +1 -1
  95. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/openapi-entities-extractor.js +1 -1
  96. package/dist/server/plugins/catalog-entities/get-server-props.js +1 -1
  97. package/dist/server/plugins/catalog-entities/plugin.js +1 -1
  98. package/dist/server/plugins/catalog-entities/schemas/database-schemas.d.ts +3 -0
  99. package/dist/server/plugins/catalog-entities/schemas/database-schemas.js +1 -1
  100. package/dist/server/plugins/catalog-entities/schemas/dto-schemas.d.ts +15 -1
  101. package/dist/server/plugins/catalog-entities/schemas/dto-schemas.js +1 -1
  102. package/dist/server/plugins/catalog-entities/schemas/read-model-schemas.d.ts +1 -0
  103. package/dist/server/plugins/catalog-entities/types/extractors.d.ts +4 -4
  104. package/dist/server/plugins/catalog-entities/utils/ajv-validator.js +1 -1
  105. package/dist/server/plugins/config-parser/loaders/content-slugs-loader.js +1 -1
  106. package/dist/server/plugins/config-parser/loaders/utils/read-and-validate-config.js +1 -1
  107. package/dist/server/plugins/default-theme/index.js +1 -1
  108. package/dist/server/plugins/mcp/docs-mcp/tools/docs-mcp-tool.d.ts +54 -0
  109. package/dist/server/plugins/mcp/docs-mcp/tools/docs-mcp-tool.js +1 -0
  110. package/dist/server/plugins/mcp/docs-mcp/tools/get-endpoint-info.d.ts +9 -8
  111. package/dist/server/plugins/mcp/docs-mcp/tools/get-endpoint-info.js +1 -1
  112. package/dist/server/plugins/mcp/docs-mcp/tools/get-endpoints.d.ts +9 -8
  113. package/dist/server/plugins/mcp/docs-mcp/tools/get-endpoints.js +1 -1
  114. package/dist/server/plugins/mcp/docs-mcp/tools/get-full-api-description.d.ts +9 -8
  115. package/dist/server/plugins/mcp/docs-mcp/tools/get-full-api-description.js +1 -1
  116. package/dist/server/plugins/mcp/docs-mcp/tools/get-security-schemes.d.ts +9 -8
  117. package/dist/server/plugins/mcp/docs-mcp/tools/get-security-schemes.js +1 -1
  118. package/dist/server/plugins/mcp/docs-mcp/tools/index.d.ts +7 -13
  119. package/dist/server/plugins/mcp/docs-mcp/tools/index.js +1 -1
  120. package/dist/server/plugins/mcp/docs-mcp/tools/list-apis.d.ts +9 -6
  121. package/dist/server/plugins/mcp/docs-mcp/tools/list-apis.js +1 -1
  122. package/dist/server/plugins/mcp/docs-mcp/tools/search.d.ts +9 -2
  123. package/dist/server/plugins/mcp/docs-mcp/tools/search.js +1 -1
  124. package/dist/server/plugins/mcp/docs-mcp/tools/utils.d.ts +2 -1
  125. package/dist/server/plugins/mcp/docs-mcp/tools/utils.js +6 -6
  126. package/dist/server/plugins/mcp/docs-mcp/tools/whoami.d.ts +9 -2
  127. package/dist/server/plugins/mcp/docs-mcp/tools/whoami.js +1 -1
  128. package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
  129. package/dist/server/plugins/mcp/handlers/errors.js +1 -1
  130. package/dist/server/plugins/mcp/handlers/handle-mcp-request.d.ts +5 -0
  131. package/dist/server/plugins/mcp/handlers/handle-mcp-request.js +1 -0
  132. package/dist/server/plugins/mcp/handlers/mcp-request-handler.d.ts +0 -1
  133. package/dist/server/plugins/mcp/handlers/mcp-request-handler.js +1 -1
  134. package/dist/server/plugins/mcp/servers/base-server.js +1 -1
  135. package/dist/server/plugins/mcp/types.d.ts +40 -0
  136. package/dist/server/plugins/mcp/workers/execute-mcp-tool.d.ts +3 -0
  137. package/dist/server/plugins/mcp/workers/execute-mcp-tool.js +1 -0
  138. package/dist/server/plugins/openapi-docs/index.js +1 -1
  139. package/dist/server/plugins/openapi-docs/template/OpenAPIDocs.js +8 -4
  140. package/dist/server/plugins/openapi-docs/template/helpers.d.ts +1 -1
  141. package/dist/server/plugins/openapi-docs/template/helpers.js +3 -3
  142. package/dist/server/plugins/scorecard-classic/loaders/scorecard-config.js +1 -1
  143. package/dist/server/plugins/scorecard-classic/types.d.ts +3 -3
  144. package/dist/server/plugins/scorecard-classic/types.js +1 -1
  145. package/dist/server/plugins/scorecards/database/repositories/local/scorecards-config-local-repository.d.ts +12 -0
  146. package/dist/server/plugins/scorecards/database/repositories/local/scorecards-config-local-repository.js +1 -0
  147. package/dist/server/plugins/scorecards/database/scorecards-config-service.d.ts +11 -0
  148. package/dist/server/plugins/scorecards/database/scorecards-config-service.js +1 -0
  149. package/dist/server/plugins/scorecards/workers/run-scorecards-worker.d.ts +2 -1
  150. package/dist/server/plugins/scorecards/workers/run-scorecards-worker.js +1 -1
  151. package/dist/server/plugins/scorecards/workers/scorecards.d.ts +1 -12
  152. package/dist/server/plugins/scorecards/workers/scorecards.js +1 -1
  153. package/dist/server/plugins/search/ai-indexer/prepare-ai-search-documents.js +1 -1
  154. package/dist/server/plugins/search/documents/search-documents.js +1 -1
  155. package/dist/server/plugins/sso/index.js +1 -1
  156. package/dist/server/providers/database/base-repository.d.ts +1 -0
  157. package/dist/server/providers/database/base-repository.js +1 -1
  158. package/dist/server/providers/database/database-connection-factory.js +1 -1
  159. package/dist/server/providers/database/databases/catalog-sqlite/migrations/0005_catalog-relations-constraint-fix.sql +2 -0
  160. package/dist/server/providers/database/databases/catalog-sqlite/migrations/0006_add-catalog-entitities-attributes-table.sql +11 -0
  161. package/dist/server/providers/database/databases/catalog-sqlite/migrations/meta/0005_snapshot.json +393 -0
  162. package/dist/server/providers/database/databases/catalog-sqlite/migrations/meta/0006_snapshot.json +458 -0
  163. package/dist/server/providers/database/databases/catalog-sqlite/migrations/meta/_journal.json +14 -0
  164. package/dist/server/providers/database/databases/catalog-sqlite/schemas/entities-attributes-table.d.ts +143 -0
  165. package/dist/server/providers/database/databases/catalog-sqlite/schemas/entities-attributes-table.js +1 -0
  166. package/dist/server/providers/database/databases/catalog-sqlite/schemas/entities-relations-table.js +1 -1
  167. package/dist/server/providers/database/databases/main-sqlite/migrations/0006_change-scorecards-config-timestamps-field-types.sql +19 -0
  168. package/dist/server/providers/database/databases/main-sqlite/migrations/meta/0006_snapshot.json +261 -0
  169. package/dist/server/providers/database/databases/main-sqlite/migrations/meta/_journal.json +7 -0
  170. package/dist/server/providers/database/databases/main-sqlite/schemas/scorecards-config-table.d.ts +24 -18
  171. package/dist/server/providers/database/databases/main-sqlite/schemas/scorecards-config-table.js +1 -1
  172. package/dist/server/providers/database/databases/sqld-sqlite/migrations/0007_catalog-relations-constraint-fix.sql +2 -0
  173. package/dist/server/providers/database/databases/sqld-sqlite/migrations/0008_add-catalog-entitities-attributes-table.sql +11 -0
  174. package/dist/server/providers/database/databases/sqld-sqlite/migrations/meta/0007_snapshot.json +833 -0
  175. package/dist/server/providers/database/databases/sqld-sqlite/migrations/meta/0008_snapshot.json +898 -0
  176. package/dist/server/providers/database/databases/sqld-sqlite/migrations/meta/_journal.json +14 -0
  177. package/dist/server/providers/database/pagination/entities-to-filter.d.ts +15 -0
  178. package/dist/server/providers/database/pagination/entities-to-filter.js +1 -0
  179. package/dist/server/providers/database/pagination/utils/index.d.ts +4 -0
  180. package/dist/server/providers/database/pagination/utils/index.js +1 -1
  181. package/dist/server/providers/database/pagination/utils/is-nested-condition.d.ts +16 -0
  182. package/dist/server/providers/database/pagination/utils/is-nested-condition.js +1 -0
  183. package/dist/server/providers/database/pagination/utils/is-simple-condition.d.ts +18 -0
  184. package/dist/server/providers/database/pagination/utils/is-simple-condition.js +1 -0
  185. package/dist/server/providers/database/pagination/utils/map-operator.d.ts +10 -0
  186. package/dist/server/providers/database/pagination/utils/map-operator.js +1 -0
  187. package/dist/server/providers/database/pagination/utils/transform-condition.d.ts +12 -0
  188. package/dist/server/providers/database/pagination/utils/transform-condition.js +1 -0
  189. package/dist/server/ssr/server-side-props/get-server-props-from-user-handler.js +1 -1
  190. package/dist/server/store.d.ts +3 -1
  191. package/dist/server/store.js +1 -1
  192. package/dist/server/tools/notifiers/logger.js +1 -1
  193. package/dist/server/tools/notifiers/reporter.js +7 -7
  194. package/dist/server/types/plugins/common.d.ts +3 -1
  195. package/dist/server/types/plugins/scorecards.d.ts +30 -0
  196. package/dist/server/types/plugins/scorecards.js +0 -0
  197. package/dist/server/utils/envs/load-env-variables.d.ts +1 -1
  198. package/dist/server/utils/envs/load-env-variables.js +1 -1
  199. package/dist/server/utils/envs/sanitize-branch-name.d.ts +6 -0
  200. package/dist/server/utils/envs/sanitize-branch-name.js +1 -0
  201. package/dist/server/utils/lifecycle-hooks.js +1 -1
  202. package/dist/server/utils/rbac.d.ts +11 -7
  203. package/dist/server/utils/rbac.js +1 -1
  204. package/dist/server/utils/time/with-timestamp.d.ts +42 -10
  205. package/dist/server/utils/time/with-timestamp.js +1 -1
  206. package/dist/server/web-server/auth.js +2 -2
  207. package/dist/server/web-server/dev-server.js +1 -1
  208. package/dist/server/web-server/handle-api-route-request.js +1 -1
  209. package/dist/server/web-server/middleware/catalogAuthMiddleware.js +1 -1
  210. package/dist/server/web-server/routes/auth.js +1 -1
  211. package/dist/server/web-server/routes/catalog/bff-catalog.js +1 -1
  212. package/dist/server/web-server/routes/catalog/catalog.js +1 -1
  213. package/dist/server/web-server/routes/dynamic-route.js +1 -1
  214. package/dist/server/web-server/routes/mcp-oauth.js +1 -1
  215. package/dist/server/web-server/routes/page-data.js +1 -1
  216. package/dist/server/workers/mcp-tool-worker-pool.d.ts +4 -0
  217. package/dist/server/workers/mcp-tool-worker-pool.js +1 -0
  218. package/dist/server/workers/mcp-tool-worker.d.ts +2 -0
  219. package/dist/server/workers/mcp-tool-worker.js +1 -0
  220. package/dist/server/workers/types.d.ts +7 -1
  221. package/dist/utils/env/is-local-development.js +1 -1
  222. package/package.json +15 -18
  223. package/dist/server/plugins/mcp/workers/run-api-routes-worker.d.ts +0 -5
  224. package/dist/server/plugins/mcp/workers/run-api-routes-worker.js +0 -1
  225. package/dist/server/workers/mcp-worker-pool.d.ts +0 -4
  226. package/dist/server/workers/mcp-worker-pool.js +0 -1
@@ -1,4 +1,4 @@
1
- import"../node-crypto-polyfill.js";import{DOMParser as U}from"@xmldom/xmldom";import{SignedXml as B}from"xml-crypto";import F from"xpath";import{deflateSync as J,inflateSync as q}from"fflate";import{createHash as H}from"crypto";import{ulid as W}from"ulid";import{AuthProviderType as u,DEFAULT_TEAM_CLAIM_NAME as K}from"@redocly/config";import{AUTH_URL as Q,JWT_SECRET_KEY as _}from"../constants/common.js";import{getPathPrefix as X,withPathPrefix as Y}from"@redocly/theme/core/utils";import{DEFAULT_AUTHENTICATED_TEAM as G,REQUIRED_OIDC_SCOPES as D,ServerRoutes as P}from"../../constants/common.js";import{appendQueryParams as Z}from"../../utils/url/append-query-params.js";import{logger as ee}from"../tools/notifiers/logger.js";import{randomString as te}from"../utils/crypto/random-string.js";import{randomUUID as R}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as I,JwtTokenExpired as ne}from"./jwt/types.js";import*as p from"./jwt/jwt.js";import{parseTeamClaimToArray as re}from"../utils/index.js";import{arrayBufferToBase64 as ae,decodeBase64 as v,encodeBase64URL as oe,urlSafeBase64 as j}from"./jwt/encode.js";import{formatSamlCertificate as se}from"./utils/format-saml-certificate.js";function N(e){return e?.type===u.OIDC}function ie(e){return e?.type===u.SAML2}async function ze(e,t){if(N(t))return ce(e,t);if(ie(t))return ue(e,t)}async function ce(e,t){const n=await V(e,t),r=new Set((t.scopes||[]).concat(D)),a=t.authorizationRequestCustomParams||{};return{type:u.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:a,pkce:t.pkce}}function ue(e,t){return{type:u.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}async function Be(e,t,n,r,a={}){const o=new Set((r.scopes||[]).concat(D));return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:r.clientId,scope:Array.from(o).join(" "),code:t,redirect_uri:E(n),grant_type:"authorization_code",...r.clientSecret?{client_secret:r.clientSecret}:{},...a}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(s=>s.json())}function de(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:a,extraParams:o,idpId:s,pkce:l},m,A,w){if(!t||!n||!r||!a)return{loginUrl:void 0};const c=new URL(t),f=w?.redirectUriOverride??`${e}${Y(P.OIDC_CALLBACK)}`,S={state:R(),idpId:s,redirectUri:f,redirectTo:m,branch:w?.branchOverride??me(e),inviteCode:A,source:w?.sourceOverride??"portal"},h={};if(l){const d=j(te(50)),g=j(H("sha256").update(d).digest("base64")),x="S256";c.searchParams.append("code_challenge",g),c.searchParams.append("code_challenge_method",x),h.code_verifier={value:d,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:X()||"/"}}}c.searchParams.append("client_id",n),c.searchParams.append("scope",a),c.searchParams.append("response_type",r),c.searchParams.append("redirect_uri",E(f)),c.searchParams.append("state",oe(JSON.stringify(S)));for(const d in o)o[d]!==void 0&&c.searchParams.append(d,o[d]);return{loginUrl:c.toString(),cookies:h}}function Fe(e,t,n,r){const a=new URL(e);return a.searchParams.append("post_logout_redirect_uri",t),r&&a.searchParams.append("state",r),a.searchParams.append("id_token_hint",n),a.toString()}async function Je(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return p.sign({type:"mcp_auth_code",client_id:e.clientId,redirect_uri:e.redirectUri,id_token:e.idToken,iat:t,exp:n},_)}async function qe(e){const{header:t,payload:n}=p.decode(e),a=Object.values(I).includes(t.alg)?t.alg:I.HS256;if(await p.verify(e,_,a),n.type!=="mcp_auth_code")throw new Error("Invalid authorization code type");if(!n.client_id||!n.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof n.exp=="number"&&Date.now()>=n.exp*1e3)throw new Error("Authorization code expired");return n}function He(e){const t=e||W(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcp_session",uri:`urn:redocly:realm:mcp:session:${n}`}}function E(e){return e.match(/^https:\/\/preview-[^\.]+--/)?"https://previewauth--"+e.split("--")[1]:e.match(/^(https:\/\/[^\.]+)--[^\.]+\.preview\./)?e.replace(/^(https:\/\/[^\.]+?)--[^\.]+\.preview\./,"$1.previewauth."):e}function me(e){return e.match(/^(https:\/\/[^\.]+)--([^\.]+)\.preview\./)?.[2]||void 0}function le(e){return e.type===u.OIDC}function pe(e){return e.type===u.SAML2}function We(e,t,n,r){return le(e)?de(t,e,n,r):pe(e)?fe(t,e,n,r):{}}function fe(e,t,n,r){const o=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
1
+ import"../node-crypto-polyfill.js";import{DOMParser as b}from"@xmldom/xmldom";import{SignedXml as B}from"xml-crypto";import F from"xpath";import{deflateSync as H,inflateSync as J}from"fflate";import{createHash as q}from"crypto";import{ulid as W}from"ulid";import{AuthProviderType as u,DEFAULT_TEAM_CLAIM_NAME as K}from"@redocly/config";import{AUTH_URL as Q,JWT_SECRET_KEY as I}from"../constants/common.js";import{getPathPrefix as X,withPathPrefix as Y}from"@redocly/theme/core/utils";import{DEFAULT_AUTHENTICATED_TEAM as G,REQUIRED_OIDC_SCOPES as D,ServerRoutes as P}from"../../constants/common.js";import{appendQueryParams as Z}from"../../utils/url/append-query-params.js";import{logger as ee}from"../tools/notifiers/logger.js";import{randomString as te}from"../utils/crypto/random-string.js";import{randomUUID as R}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as y,JwtTokenExpired as ne}from"./jwt/types.js";import*as p from"./jwt/jwt.js";import{parseTeamClaimToArray as re}from"../utils/index.js";import{arrayBufferToBase64 as ae,decodeBase64 as v,encodeBase64URL as oe,urlSafeBase64 as j}from"./jwt/encode.js";import{formatSamlCertificate as se}from"./utils/format-saml-certificate.js";function N(e){return e?.type===u.OIDC}function ie(e){return e?.type===u.SAML2}async function ze(e,t){if(N(t))return ce(e,t);if(ie(t))return ue(e,t)}async function ce(e,t){const r=await V(e,t),n=new Set((t.scopes||[]).concat(D)),a=t.authorizationRequestCustomParams||{};return{type:u.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:r.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(n).join(" "),extraParams:a,pkce:t.pkce}}function ue(e,t){return{type:u.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}async function Be(e,t,r,n,a={}){const o=new Set((n.scopes||[]).concat(D));return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:n.clientId,scope:Array.from(o).join(" "),code:t,redirect_uri:E(r),grant_type:"authorization_code",...n.clientSecret?{client_secret:n.clientSecret}:{},...a}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(s=>s.json())}function de(e,{authorizationEndpoint:t,clientId:r,responseType:n,scope:a,extraParams:o,idpId:s,pkce:l},m,A,S){if(!t||!r||!n||!a)return{loginUrl:void 0};const c=new URL(t),f=S?.redirectUriOverride??`${e}${Y(P.OIDC_CALLBACK)}`,x={state:R(),idpId:s,redirectUri:f,redirectTo:m,branch:S?.branchOverride??me(e),inviteCode:A,source:S?.sourceOverride??"portal"},h={};if(l){const d=j(te(50)),_=j(q("sha256").update(d).digest("base64")),g="S256";c.searchParams.append("code_challenge",_),c.searchParams.append("code_challenge_method",g),h.code_verifier={value:d,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:X()||"/"}}}c.searchParams.append("client_id",r),c.searchParams.append("scope",a),c.searchParams.append("response_type",n),c.searchParams.append("redirect_uri",E(f)),c.searchParams.append("state",oe(JSON.stringify(x)));for(const d in o)o[d]!==void 0&&c.searchParams.append(d,o[d]);return{loginUrl:c.toString(),cookies:h}}function Fe(e,t,r,n){const a=new URL(e);return a.searchParams.append("post_logout_redirect_uri",t),n&&a.searchParams.append("state",n),a.searchParams.append("id_token_hint",r),a.toString()}async function He(e){const t=Math.floor(Date.now()/1e3),r=t+(e.ttlSec??600);return p.sign({type:"mcp_auth_code",client_id:e.clientId,redirect_uri:e.redirectUri,id_token:e.idToken,iat:t,exp:r},I,y.HS256)}async function Je(e){await p.verify(e,I,y.HS256);const{payload:t}=p.decode(e);if(t.type!=="mcp_auth_code")throw new Error("Invalid authorization code type");if(!t.client_id||!t.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof t.exp=="number"&&Date.now()>=t.exp*1e3)throw new Error("Authorization code expired");return t}function qe(e){const t=e||W(),r=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:r,object:"mcp_session",uri:`urn:redocly:realm:mcp:session:${r}`}}function E(e){return e.match(/^https:\/\/preview-[^\.]+--/)?"https://previewauth--"+e.split("--")[1]:e.match(/^(https:\/\/[^\.]+)--[^\.]+\.preview\./)?e.replace(/^(https:\/\/[^\.]+?)--[^\.]+\.preview\./,"$1.previewauth."):e}function me(e){return e.match(/^(https:\/\/[^\.]+)--([^\.]+)\.preview\./)?.[2]||void 0}function le(e){return e.type===u.OIDC}function pe(e){return e.type===u.SAML2}function We(e,t,r,n){return le(e)?de(t,e,r,n):pe(e)?fe(t,e,r,n):{}}function fe(e,t,r,n){const o=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
2
2
  xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
3
3
  Version="2.0"
4
4
  ID="_${R()}"
@@ -9,4 +9,4 @@ import"../node-crypto-polyfill.js";import{DOMParser as U}from"@xmldom/xmldom";im
9
9
  <samlp:NameIDPolicy
10
10
  AllowCreate="true"
11
11
  Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
12
- </samlp:AuthnRequest>`,s=he(o);return{loginUrl:Z(t.ssoUrl,{SAMLRequest:s,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal"})})}}function he(e){return ae(J(new TextEncoder().encode(e)).buffer)}function Ke(e){const t=v(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=q(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function Qe(e){try{return JSON.parse(v(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function Xe(e){const t=new U().parseFromString(e,"application/xml"),r=i(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,o=i(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",s=i(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],l=s&&s.nodeValue||void 0,m=i(t,"//*[local-name(.)='Audience']/text()")[0],A=m&&m.nodeValue||void 0,c=i(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",f=i(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],S=f&&f.nodeValue||"",h=i(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],d=h&&h.nodeValue||"",g=i(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],x=ye(g),k={},T=i(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(T.length)for(const C of T){const O=i(C,"./@Name")[0];if(O.nodeValue){const b=i(C,"./*[local-name(.)='AttributeValue']/text()")[0];b?.nodeValue&&(k[O.nodeValue]=b.nodeValue)}}return{uid:S,success:r,expiresAt:x,issuerId:l,entityId:A,attrs:k,cert:c,nameFormat:d,destination:o}}function ye(e){const t=typeof e?.nodeValue=="string"&&L(Date.parse(e.nodeValue)),n=L(Date.now()),r=L(Date.now()+720*60*1e3);return t?t>n&&t<r?r:t:n}function L(e){return Math.floor(e/1e3)}const M={},y={jwks:{}};async function V(e,t){return M[e]||(M[e]=t.configurationUrl?await $(t.configurationUrl):t.configuration),M[e]}async function we(e){for(const t of Object.keys(e)){const n=e[t];if(!N(n))continue;const r=await V(t,n);if(r.jwks_uri){const a=await $(r.jwks_uri);for(const o of a.keys)y.jwks[o.kid]={...o,idpId:t}}}}async function $(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function Ye(e){return fetch(`${Q}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function Ge(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com","cba.au.redocly.com"].some(r=>Ae(t.hostname,r))}function Ae(e,t){return e===t||e.endsWith(`.${t}`)}async function Ze(e,t){const n=new U().parseFromString(e),r=i(n,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!r)throw new Error("Cannot find Signature in the SAML response");const a=se(t),o=new B({publicCert:a});o.loadSignature(r);try{return o.checkSignature(e)}catch{return!1}}function et(e,t,n,r){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=n["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let a;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(a=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/.+@.+/)&&(a=e);const o=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],s=o?.match(/.+@.+/);return a=a||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(s?o:void 0),a=a?.toLowerCase(),{sub:e,given_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:n["http://schemas.microsoft.com/identity/claims/displayname"]||o,email:a,email_verified:!0,teams:r?re(n[r]):[]}}function z(e,t={}){return e.map(n=>t[n]||n)}async function tt(e,t){if(!t)return{};const n=t.authorization;if(!n)return{};try{const r=p.decode(n);if(r.header.alg===I.RS256){y.jwks[r.header.kid]===void 0&&await we(e);const m=y.jwks[r.header.kid];if(!m)return y.jwks[r.header.kid]=null,{};await p.verify(n,m,r.header.alg)}else await p.verify(n,_,r.header.alg);const a=r.payload.idpId||y.jwks[r.header.kid]?.idpId,o=e[a]||{},s=xe(o),l=ge(o);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:a,teams:Array.from(new Set([...z(r.payload.teams||[],l),..."defaultTeams"in o&&o.defaultTeams||[],...z("teamsClaimName"in o&&r.payload[s||""]||[],l),G])),name:Se(r.payload),isAuthenticated:!0,idpAccessToken:t.idp_access_token,federatedAccessToken:t.federated_access_token,federatedIdToken:t.federated_id_token,authCookie:n}}catch(r){r instanceof ne||ee.error("Malformed JWT token: %s",r.message)}return{}}function Se(e){return e.name||e.given_name||e.email}function ge(e){switch(e.type){case u.SAML2:return e.teamsAttributeMap;case u.OIDC:return e.teamsClaimMap;default:return}}function xe(e){switch(e.type){case u.SAML2:return e.teamsAttributeName;case u.OIDC:return e.teamsClaimName;default:return K}}function i(e,t){return F.select(t,e)||[]}export{We as buildLoginUrl,de as buildOidcLoginUrl,Fe as buildOidcLogoutUrl,fe as buildSAML2LoginUrl,Je as createMcpAuthorizationCode,He as createMcpSessionResource,Ke as decodeSamlResponse,he as encodeSAML2,et as extractUserClaims,ze as getAuthProviderLoginParams,ce as getOidcLoginParams,V as getOidcMetadata,Ye as getRedoclyTokenPayload,ue as getSaml2LoginParams,tt as getUserParamsFromCookies,Se as getUsernameFromPayload,N as isOidcProviderConfig,Ge as isRedoclySso,ie as isSaml2ProviderConfig,Be as oidcExchangeCodeForToken,Qe as parseOidcState,me as parsePreviewBranch,Xe as parseSamlResponse,E as rewritePreviewAuthRedirectUri,qe as verifyMcpAuthorizationCode,Ze as verifySAMLResponse};
12
+ </samlp:AuthnRequest>`,s=he(o);return{loginUrl:Z(t.ssoUrl,{SAMLRequest:s,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:r,inviteCode:n,source:"portal"})})}}function he(e){return ae(H(new TextEncoder().encode(e)).buffer)}function Ke(e){const t=v(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const r=J(new Uint8Array(atob(e).split("").map(n=>n.charCodeAt(0))));return new TextDecoder().decode(r)}function Qe(e){try{return JSON.parse(v(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function Xe(e){const t=new b().parseFromString(e,"application/xml"),n=i(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,o=i(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",s=i(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],l=s&&s.nodeValue||void 0,m=i(t,"//*[local-name(.)='Audience']/text()")[0],A=m&&m.nodeValue||void 0,c=i(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",f=i(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],x=f&&f.nodeValue||"",h=i(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],d=h&&h.nodeValue||"",_=i(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],g=ye(_),k={},T=i(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(T.length)for(const C of T){const O=i(C,"./@Name")[0];if(O.nodeValue){const U=i(C,"./*[local-name(.)='AttributeValue']/text()")[0];U?.nodeValue&&(k[O.nodeValue]=U.nodeValue)}}return{uid:x,success:n,expiresAt:g,issuerId:l,entityId:A,attrs:k,cert:c,nameFormat:d,destination:o}}function ye(e){const t=typeof e?.nodeValue=="string"&&L(Date.parse(e.nodeValue)),r=L(Date.now()),n=L(Date.now()+720*60*1e3);return t?t>r&&t<n?n:t:r}function L(e){return Math.floor(e/1e3)}const M={},w={jwks:{}};async function V(e,t){return M[e]||(M[e]=t.configurationUrl?await $(t.configurationUrl):t.configuration),M[e]}async function we(e){for(const t of Object.keys(e)){const r=e[t];if(!N(r))continue;const n=await V(t,r);if(n.jwks_uri){const a=await $(n.jwks_uri);for(const o of a.keys)w.jwks[o.kid]={...o,idpId:t}}}}async function $(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function Ye(e){return fetch(`${Q}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function Ge(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com","cba.au.redocly.com"].some(n=>Se(t.hostname,n))}function Se(e,t){return e===t||e.endsWith(`.${t}`)}async function Ze(e,t){const r=new b().parseFromString(e),n=i(r,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!n)throw new Error("Cannot find Signature in the SAML response");const a=se(t),o=new B({publicCert:a});o.loadSignature(n);try{return o.checkSignature(e)}catch{return!1}}function et(e,t,r,n){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=r["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let a;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(a=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/.+@.+/)&&(a=e);const o=r["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],s=o?.match(/.+@.+/);return a=a||r["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(s?o:void 0),a=a?.toLowerCase(),{sub:e,given_name:r["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:r["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:r["http://schemas.microsoft.com/identity/claims/displayname"]||o,email:a,email_verified:!0,teams:n?re(r[n]):[]}}function z(e,t={}){return e.map(r=>t[r]||r)}async function tt(e,t){if(!t)return{};const r=t.authorization;if(!r)return{};try{const n=p.decode(r);if(n.header.alg===y.RS256){w.jwks[n.header.kid]===void 0&&await we(e);const m=w.jwks[n.header.kid];if(!m)return w.jwks[n.header.kid]=null,{};await p.verify(r,m,y.RS256)}else await p.verify(r,I,y.HS256);const a=n.payload.idpId||w.jwks[n.header.kid]?.idpId,o=e[a]||{},s=_e(o),l=xe(o);return{...n.payload,email:n.payload.email?.toLowerCase(),idpId:a,teams:Array.from(new Set([...z(n.payload.teams||[],l),..."defaultTeams"in o&&o.defaultTeams||[],...z("teamsClaimName"in o&&n.payload[s||""]||[],l),G])),name:Ae(n.payload),isAuthenticated:!0,idpAccessToken:t.idp_access_token,federatedAccessToken:t.federated_access_token,federatedIdToken:t.federated_id_token,authCookie:r}}catch(n){n instanceof ne||ee.error("Malformed JWT token: %s",n.message)}return{}}function Ae(e){return e.name||e.given_name||e.email}function xe(e){switch(e.type){case u.SAML2:return e.teamsAttributeMap;case u.OIDC:return e.teamsClaimMap;default:return}}function _e(e){switch(e.type){case u.SAML2:return e.teamsAttributeName;case u.OIDC:return e.teamsClaimName;default:return K}}function i(e,t){return F.select(t,e)||[]}export{We as buildLoginUrl,de as buildOidcLoginUrl,Fe as buildOidcLogoutUrl,fe as buildSAML2LoginUrl,He as createMcpAuthorizationCode,qe as createMcpSessionResource,Ke as decodeSamlResponse,he as encodeSAML2,et as extractUserClaims,ze as getAuthProviderLoginParams,ce as getOidcLoginParams,V as getOidcMetadata,Ye as getRedoclyTokenPayload,ue as getSaml2LoginParams,tt as getUserParamsFromCookies,Ae as getUsernameFromPayload,N as isOidcProviderConfig,Ge as isRedoclySso,ie as isSaml2ProviderConfig,Be as oidcExchangeCodeForToken,Qe as parseOidcState,me as parsePreviewBranch,Xe as parseSamlResponse,E as rewritePreviewAuthRedirectUri,Je as verifyMcpAuthorizationCode,Ze as verifySAMLResponse};
@@ -1 +1 @@
1
- import{ServerRoutes as c}from"../../constants/common.js";import{reporter as n}from"../tools/notifiers/reporter.js";import{telemetry as s}from"../telemetry/index.js";import{installDevRoutes as p,installProdRoutes as f}from"./routes/index.js";import{createRouter as v}from"./router.js";import{readStaticAsset as l}from"./node-asset-reader.js";import{startHttpServer as u}from"./http.js";import{listenStore as S}from"./store-ws.js";import{ejectComponentDataHandler as D}from"./routes/eject.js";import{attachWsServer as d}from"./ws.js";import{DatabasePreconnectService as w}from"../providers/database/database-preconnect-service.js";import{KvService as E}from"../persistence/kv/services/kv-service.js";import{runScorecardsWorker as R}from"../plugins/scorecards/workers/run-scorecards-worker.js";import{isCatalogEntitiesEnabled as b}from"../utils/is-catalog-entities-enabled.js";import{isScorecardsEnabled as C}from"../utils/is-scorecards-enabled.js";async function J(r,o,i){s.initialize(!0);const{port:a=4e3}=i,e=v();if(e.get(c.EJECT_COMPONENT,D(r)),p(e,r),f(e,r,{readStaticAsset:l,resolveRouteData:t=>r.resolveRouteStaticData(t,o)}),b()){await w.init(r.serverOutDir);const t=await E.getInstance({baseDbDir:r.serverOutDir});setInterval(()=>t.clearExpired(),300*1e3)}try{await r.userCodeReady;const t=await u(e,a),m=d(t);S(r,m),C(r.config)&&await R(r.serverOutDir)}catch(t){await n.panic(t)}}export{J as startDevServer};
1
+ import{ServerRoutes as c}from"../../constants/common.js";import{reporter as n}from"../tools/notifiers/reporter.js";import{telemetry as s}from"../telemetry/index.js";import{installDevRoutes as p,installProdRoutes as f}from"./routes/index.js";import{createRouter as v}from"./router.js";import{readStaticAsset as l}from"./node-asset-reader.js";import{startHttpServer as u}from"./http.js";import{listenStore as S}from"./store-ws.js";import{ejectComponentDataHandler as d}from"./routes/eject.js";import{attachWsServer as D}from"./ws.js";import{DatabasePreconnectService as w}from"../providers/database/database-preconnect-service.js";import{KvService as E}from"../persistence/kv/services/kv-service.js";import{runScorecardsWorker as R}from"../plugins/scorecards/workers/run-scorecards-worker.js";import{isCatalogEntitiesEnabled as b}from"../utils/is-catalog-entities-enabled.js";import{isScorecardsEnabled as g}from"../utils/is-scorecards-enabled.js";async function J(r,o,i){s.initialize(!0);const{port:a=4e3}=i,e=v();if(e.get(c.EJECT_COMPONENT,d(r)),p(e,r),f(e,r,{readStaticAsset:l,resolveRouteData:t=>r.resolveRouteStaticData(t,o)}),b()){await w.init(r.serverOutDir);const t=await E.getInstance({baseDbDir:r.serverOutDir});setInterval(()=>t.clearExpired(),300*1e3)}try{await r.userCodeReady;const t=await u(e,a),m=D(t);S(r,m),g(r.config)&&await R(r.serverOutDir,r.config.scorecards)}catch(t){await n.panic(t)}}export{J as startDevServer};
@@ -1 +1 @@
1
- import{logger as n}from"../tools/notifiers/logger.js";import{runApiRoutesWorker as o}from"../api-routes/run-api-routes-worker.js";import{runMcpWorker as m}from"../plugins/mcp/workers/run-api-routes-worker.js";import{MCP_DOCS_SERVER_HANDLER_ID as i}from"../plugins/mcp/index.js";async function f(t,s,a){try{const e=n.startTiming(),r=t.requestHandlerId===i?await m(t,s,a):await o(t,s,a);return n.infoTime(e,`API request handled "${new URL(s.req.url).pathname}" with status "${r.status}" and content type "${r.headers?.get("content-type")}"`),r.headers.set("X-Source","REALM_API_FUNCTION"),r}catch(e){return n.error(`[${t.requestHandlerId}] ${e.stack}`),e.name==="RequestBodySizeLimitError"?s.json({message:e.message},{status:413}):e.name==="ResponseSizeLimitError"?s.json({message:e.message},{status:500}):e.name==="MemoryUsageLimitError"?s.json({message:e.message},{status:502}):e.name==="TimeoutExceededError"?s.json({message:e.message},{status:504}):process.env.NODE_ENV==="development"?s.json({message:e.message},{status:500}):s.json({message:"Internal server error"},{status:500})}}export{f as handleApiRouteRequest};
1
+ import{logger as n}from"../tools/notifiers/logger.js";import{runApiRoutesWorker as o}from"../api-routes/run-api-routes-worker.js";import{MCP_DOCS_SERVER_HANDLER_ID as m}from"../plugins/mcp/index.js";import{handleMcpRequest as i}from"../plugins/mcp/handlers/handle-mcp-request.js";async function f(t,s,a){try{const e=n.startTiming(),r=t.requestHandlerId===m?await i(t,s,a):await o(t,s,a);return n.infoTime(e,`API request handled "${new URL(s.req.url).pathname}" with status "${r.status}" and content type "${r.headers?.get("content-type")}"`),r.headers.set("X-Source","REALM_API_FUNCTION"),r}catch(e){return n.error(`[${t.requestHandlerId}] ${e.stack}`),e.name==="RequestBodySizeLimitError"?s.json({message:e.message},{status:413}):e.name==="ResponseSizeLimitError"?s.json({message:e.message},{status:500}):e.name==="MemoryUsageLimitError"?s.json({message:e.message},{status:502}):e.name==="TimeoutExceededError"?s.json({message:e.message},{status:504}):process.env.NODE_ENV==="development"?s.json({message:e.message},{status:500}):s.json({message:"Internal server error"},{status:500})}}export{f as handleApiRouteRequest};
@@ -1 +1 @@
1
- import{KvService as c}from"../../persistence/kv/services/kv-service.js";import{JWT_SECRET_KEY as d}from"../../constants/common.js";import*as s from"../jwt/jwt.js";const u=1440*60,y=(e,a)=>e.secureMethods?.includes(a)??!1;function w(e,a={}){return async(r,t)=>{const n=r.req.method;return y(a,n)?await l(r,t,e):await t()}}const l=async(e,a,r)=>{const t=e.req.header("apiKey");if(t)return await p(e,a,t,r);const i=e.req.header("authorization")?.replace("Bearer ","");return i?await f(e,a,i):e.json({message:"API key is required"},401)},p=async(e,a,r,t)=>{if(!process.env.BH_API_URL)return e.json({message:"API key validation service not configured"},500);try{const n=await c.getInstance({baseDbDir:t});if(await n.get(["api-keys","reunite",r]))return await a();const o=new URL("/api/api-keys-verify",process.env.BH_API_URL).toString();return(await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:r})})).ok?(await n.set(["api-keys","reunite",r],{valid:!0},{ttlInSeconds:u}),await a()):e.json({message:"Invalid API key"},401)}catch{return e.json({message:"API key validation failed"},400)}},f=async(e,a,r)=>{try{const t=await s.verify(r,d),n=s.decode(r).payload.isInternalConnection;return!t||!n?e.json({message:"API key is required"},401):await a()}catch{return e.json({message:"API key validation failed"},400)}};export{w as catalogAuthMiddleware};
1
+ import{KvService as c}from"../../persistence/kv/services/kv-service.js";import{JWT_SECRET_KEY as d}from"../../constants/common.js";import*as o from"../jwt/jwt.js";import{AlgorithmTypes as u}from"../jwt/types.js";const y=1440*60,p=(e,r)=>e.secureMethods?.includes(r)??!1;function g(e,r={}){return async(a,t)=>{const n=a.req.method;return p(r,n)?await l(a,t,e):await t()}}const l=async(e,r,a)=>{const t=e.req.header("apiKey");if(t)return await f(e,r,t,a);const i=e.req.header("authorization")?.replace("Bearer ","");return i?await m(e,r,i):e.json({message:"API key is required"},401)},f=async(e,r,a,t)=>{if(!process.env.BH_API_URL)return e.json({message:"API key validation service not configured"},500);try{const n=await c.getInstance({baseDbDir:t});if(await n.get(["api-keys","reunite",a]))return await r();const s=new URL("/api/api-keys-verify",process.env.BH_API_URL).toString();return(await fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:a})})).ok?(await n.set(["api-keys","reunite",a],{valid:!0},{ttlInSeconds:y}),await r()):e.json({message:"Invalid API key"},401)}catch{return e.json({message:"API key validation failed"},400)}},m=async(e,r,a)=>{try{const t=await o.verify(a,d,u.HS256),n=o.decode(a).payload.isInternalConnection;return!t||!n?e.json({message:"API key is required"},401):await r()}catch{return e.json({message:"API key validation failed"},400)}};export{g as catalogAuthMiddleware};
@@ -1 +1 @@
1
- import{setCookie as L,deleteCookie as T}from"hono/cookie";import{AuthProviderType as G}from"@redocly/config";import{withPathPrefix as R,getPathPrefix as _}from"@redocly/theme/core/utils";import{compareURIs as X}from"../../../utils/url/compare-uris.js";import{ensureArray as q}from"../../../utils/array/ensure-array.js";import{ALTERNATIVE_AUD_CLAIM_NAME as E,JWT_SECRET_KEY as v,ORG_SLUG as W}from"../../constants/common.js";import{DEFAULT_COOKIE_EXPIRATION as F,ServerRoutes as D}from"../../../constants/common.js";import{sanitizeRedirectPathname as B}from"../../../utils/url/sanitize-redirect-pathname.js";import{telemetry as k}from"../../telemetry/index.js";import{getAuthProviderLoginParams as Y,isOidcProviderConfig as $,isSaml2ProviderConfig as Q,oidcExchangeCodeForToken as Z,buildLoginUrl as x,decodeSamlResponse as ee,extractUserClaims as re,parseSamlResponse as oe,parseOidcState as ne,verifySAMLResponse as te,getUsernameFromPayload as ie,buildOidcLogoutUrl as se,getOidcMetadata as z,getRedoclyTokenPayload as ae,isRedoclySso as de,rewritePreviewAuthRedirectUri as ce,parsePreviewBranch as N,buildOidcLoginUrl as le,createMcpSessionResource as I}from"../auth.js";import*as S from"../jwt/jwt.js";import{AlgorithmTypes as ue}from"../jwt/types.js";import{handleErrorPageRender as pe}from"../utils.js";import{encodeBase64URL as ge}from"../jwt/encode.js";async function Oe(i){if(process.env.NODE_ENV==="production")return i.newResponse(null,404,{});const{password:e,...r}=await i.req.json(),a=await S.sign({...r,name:r.username||r.email||"Unknown"},v);return L(i,"authorization",a,{path:_()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),i.newResponse(null,200,{})}function Pe(){return async i=>{const e=i.get("logger"),r=encodeURIComponent(i.req.query("message")||"");e.error(`Login error: ${r}`);const a=`${D.LOGIN}/?error=${encodeURIComponent(r)}`;return i.newResponse(null,301,{Location:a})}}function H(i){if(!i||!i.includes(D.MCP_CALLBACK))return null;try{const e=i.split("/"),r=e[e.length-1];if(r){const a=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(a).mcpSessionId||null}}catch{}return null}function Se(i){return async e=>{const r=e.get("logger"),a=i.getConfig().ssoDirect,n=ne(e.req.query("state")),f=n.idpId,t=n.source==="mcp"||n.redirectTo&&typeof n.redirectTo=="string"&&n.redirectTo.includes(D.MCP_CALLBACK),c=t?H(typeof n.redirectTo=="string"?n.redirectTo:void 0):null,s=a?.[f];if(!$(s))return r.error("OIDC login error: missing OIDC provider config"),e.text("Forbidden",403);const d=await z(f,s);if(a&&!d.token_endpoint){const p="Invalid OIDC configuration: token_endpoint is required";return r.error(`OIDC login error: ${p}`),e.text(p,500)}try{const p=d.token_endpoint,l=e.req.query("code"),m=e.req.query("error");if(m)return t&&k.sendMcpAuthorizationFailedMessage({...I(c),error:`OIDC error: ${m}`,error_details:e.req.query("error_description")||null}),pe(e,i,{slug:"/"},403,"403OIDC");if(!l){const h="Code is expected but not present";return r.error(`OIDC login error: ${h}`),t&&k.sendMcpAuthorizationFailedMessage({...I(c),error:h,error_details:null}),new Response(`Forbidden: ${h}`,{status:403})}const y=e.req.header("x-forwarded-host"),g=e.req.header("x-forwarded-proto")||"https",A=t&&typeof n.redirectUri=="string"?n.redirectUri:new URL(R(D.OIDC_CALLBACK),y?`${g}://${y}`:e.req.url).toString(),C=e.get("cookies")?.code_verifier,u=await Z(p,l,A,s,{...s.tokenRequestCustomParams,...C?{code_verifier:C}:{}});if(u.error)return r.error(`Error from OIDC provider: "${u.error}"`),t&&k.sendMcpAuthorizationFailedMessage({...I(c),error:`Token exchange error: ${u.error}`,error_details:u.error_description||null}),e.text(`Forbidden: ${u.error_description||u.error}`,403);if(!u?.id_token){const h="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${h}`),t&&k.sendMcpAuthorizationFailedMessage({...I(c),error:h,error_details:null}),new Response(`Forbidden: ${h}`,{status:403})}const{payload:o,header:U}=S.decode(u.id_token),j=U.alg===ue.RS256;if(s.audience?.length&&![...q(o.aud||[]),...q(o[E]||[])].some(M=>s.audience?.includes(M))){const M="No valid audience found in id_token";return r.error(`OIDC login error: ${M}`),t&&k.sendMcpAuthorizationFailedMessage({...I(c),error:M,error_details:null}),new Response(`Forbidden: ${M}`)}const b=j?u.id_token:await S.sign({...o,idpId:f},v);ie(o)||r.warn("To display your username, the required 'email' or 'full_profile' scope must be added to the identity provider configuration");const O=s?.tokenExpirationTime?Date.now()+s.tokenExpirationTime*1e3:o.exp*1e3||Date.now()+F*1e3;if(s.introspectEndpoint){const h=await fetch(s.introspectEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:u.access_token})});if(h.ok){const P=(await h.json()).ext?.federatedIdentity;P&&(L(e,"federated_access_token",P.access_token||"",{path:_()||"/",httpOnly:!1,expires:new Date(O)}),L(e,"federated_id_token",P.id_token||"",{path:_()||"/",httpOnly:!1,expires:new Date(O)}))}else r.warn(`OIDC introspect error: ${h.statusText}`)}if(L(e,"authorization",b,{path:_()||"/",httpOnly:!0,expires:new Date(O)}),b!==u.id_token&&L(e,"idp_id_token",u.id_token||"",{path:_()||"/",httpOnly:!0,expires:new Date(O)}),L(e,"idp_access_token",u.access_token||"",{path:_()||"/",httpOnly:!0,expires:new Date(O)}),T(e,"code_verifier",{path:_()||"/"}),t&&n.redirectTo&&typeof n.redirectTo=="string"&&n.redirectTo.includes(D.MCP_CALLBACK)){const h=e.req.url.split("?")[0].replace(D.OIDC_CALLBACK,""),M=R(n.redirectTo),P=`${h}${M}`;return e.newResponse(null,302,{Location:P})}const K=typeof n.redirectTo=="string"?n.redirectTo:void 0;let J=B(new URL(K||"/",e.req.url).pathname);const V=e.newResponse(null,302,{Location:J});return r.updateContext({email:o.email,subject:o.sub}),r.info("OIDC login successful"),V}catch(p){const l=p instanceof Error?p.message:String(p),m=p instanceof Error?p.stack:String(p);if(r.error(`OIDC login error: ${l}`),t&&k.sendMcpAuthorizationFailedMessage({...I(c),error:l,error_details:m}),p.error==="access_denied")return r.info("Access denied"),e.text("Forbidden",403)}const w="Something went wrong";return r.error(`OIDC login error: ${w}`),t&&k.sendMcpAuthorizationFailedMessage({...I(c),error:w,error_details:null}),e.text(w,500)}}function ve(i){return async e=>{const r=e.get("logger"),n=e.get("auth").claims?.idpId,t=i.getConfig().ssoDirect?.[n];if(e.req.method==="POST")return $(t)||T(e,"authorization",{path:_()||"/"}),r.info("Logout successful"),e.newResponse(null,200,{});let c;if($(t)){const s=(await z(n,t)).end_session_endpoint;if(s){const d=new URL(e.req.url),w=e.req.header("x-forwarded-proto")||d.protocol.slice(0,-1)||"https",p=e.req.header("x-forwarded-host")||d.host,l=`${w}://${p}`,m=N(l),y=m?ge(JSON.stringify({branch:N(l)})):void 0,g=m?`${ce(l)}/_auth/logout`:`${l}/post-logout`;c=se(s,g,e.get("cookies")?.idp_id_token||e.get("cookies")?.authorization||"",y)}}return r.info("Logout successful"),T(e,"authorization",{path:_()||"/"}),e.newResponse(null,302,{Location:c||R("/")})}}function $e(i){return async e=>{const r=i.getConfig().logoutReturnUrl,a=r||R("/");return e.newResponse(null,302,{Location:a})}}function Ue(i){return async e=>{const r=e.get("logger"),a=e.req.param("code"),n=process.env.BH_API_URL,f=(t,c,s)=>t&&c?`${t} ${c.charAt(0)}`:s;try{if(!n)throw new Error("BH_API_URL is not set");const t=i.getConfig().ssoDirect;if(!t||!Object.keys(t).length)return r.warn("Invite no sso configured to handle"),e.redirect(R("/"));const c=await fetch(`${n}/user-invites/public/${a}`);if(!c.ok)return c.status===404?(r.warn(`Invite ${a} not found redirect to homepage`),e.redirect(R("/"))):(r.error("Invite error",await c.text()),e.redirect(R("/")));const s=await c.json(),d=new URL(R("/invite"),e.req.url);return d.searchParams.set("code",a),d.searchParams.set("org",s.organization.name),d.searchParams.set("invitedBy",f(s.invitedBy.firstName,s.invitedBy.lastName,s.invitedBy.name)),e.newResponse(null,302,{Location:d.toString()})}catch(t){return r.error("Error processing invite",{error:t,inviteCode:a}),e.text(t.message||"Failed to process invite",400)}}}function Te(i){return async e=>{const r=e.get("logger"),a=i.getConfig().ssoDirect,n=new URL(e.req.url),f=e.req.query("inviteCode"),t=e.req.header("x-forwarded-proto")||n.protocol.slice(0,-1)||"https",c=e.req.header("x-forwarded-host")||n.host,s=`${t}://${c}`;let d=n.searchParams.get("idpId");const w=n.searchParams.get("redirectTo"),p=Object.keys(a||{})[0];d=d||p;const l=n.searchParams.get("mcp_redirect_uri"),m=!!l;if(!a?.[d]){const o="Invalid idpId";if(r.error(`IdP login error: ${o}`),m){const U=H(w||void 0);k.sendMcpAuthorizationFailedMessage({...I(U),error:o,error_details:null})}return e.text(`Forbidden: ${o}`,403)}const g=d&&a?await Y(d,a[d]):void 0,A={};for(const o of Object.keys(g?.extraParams||{}))A[o]=n.searchParams.get(o)||g?.extraParams?.[o]||void 0;let C,u={};if(m&&l&&g&&g.type===G.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${l}`);const o=le("",{...g,extraParams:A},w,f,{redirectUriOverride:l,sourceOverride:"mcp",branchOverride:void 0});C=o.loginUrl,u=o.cookies||{}}else if(g){const o=x({...g,extraParams:A},s,w,f);C=o.loginUrl,u=o.cookies||{}}return Object.keys(u).forEach(o=>{L(e,o,u[o].value,u[o].options)}),r.info(`IdP login initiated for ID '${d}'`),e.newResponse(null,302,{Location:C||new URL(e.req.url).pathname})}}function qe(i){return async e=>{const r=e.get("logger"),a=await e.req.formData(),n=a.get("SAMLResponse"),f=a.get("RelayState");if(typeof n!="string"||typeof f!="string"){const o="SAMLResponse is required";return r.error(`SAML2 login error: ${o}`),e.text(`Bad request: ${o}`,400)}const t=ee(n),{success:c,uid:s,nameFormat:d,attrs:w,issuerId:p,expiresAt:l}=oe(t),{idpId:m,redirectTo:y}=JSON.parse(f);if(!c){const o="SAML2 assertion is not successful";return r.error(`SAML2 login error: ${o}`),e.text(`Permission denied: ${o}`,401)}if(!l||Math.ceil(Date.now()/1e3)>=l){const o="SAML2 Token Expired";return r.error(`SAML2 login error: ${o}`),e.text(o,401)}const g=i.getConfig().ssoDirect?.[m];if(!g||!Q(g)){const o="Cannot find valid IdP";return r.error(`SAML2 login error: ${o}`),e.text(`Permission denied: ${o}`,401)}if(!(g.issuerId&&p&&X(g.issuerId,p))){const o="IssuerID is misconfigured or untrusted assertions issuer received";return r.error(`SAML2 login error: ${o}`),e.text(`Permission denied: ${o}`,401)}if(!await te(t,g.x509PublicCert)){const o="SAMLResponse signature invalid";return r.error(`SAML2 login error: ${o}`),e.text(o,401)}const C=re(s,d,w,g.teamsAttributeName);if(!C.sub){const o="The provider did not return a valid user identity.";return r.error(`SAML2 login error: ${o}`),e.text(o,400)}if(!C.email){const o="The provider did not return a valid user email.";return r.error(`SAML2 login error: ${o}`),e.text(o,400)}const u=await S.sign({...C,idpId:m},v);return L(e,"authorization",u,{path:_()||"/",httpOnly:!0,expires:new Date(l*1e3)}),r.updateContext({email:C.email,subject:C.sub}),r.info("SAML2 login successful"),e.newResponse(null,302,{Location:y||"/"})}}function be(i){return async e=>{const r=e.get("logger"),a=new URL(e.req.query("redirectTo")||"/",e.req.url),n=R(B(a.pathname)),f=i.getConfig().ssoDirect,t=Object.entries(f||{}).find(([,y])=>$(y)&&de(y));if(!(f&&t))return e.newResponse(null,302,{Location:n});const s=e.req.query("token"),d=s&&await ae(s);if(!d)return e.newResponse(null,302,{Location:n});if(!q(d[E]||[]).some(y=>y===W))return e.newResponse(null,302,{Location:n});const l=await S.sign({...d,idpId:t?.at(0)},v),m=Date.now()+F*1e3;return L(e,"authorization",l,{path:_()||"/",httpOnly:!0,expires:new Date(m),sameSite:"None",secure:!0}),r.info("Token login successful"),e.newResponse(null,302,{Location:n})}}export{Oe as authorizeHandler,Te as idpLoginHandler,Ue as inviteHandler,ve as logoutHandler,Se as oidcCallbackHandler,$e as postLogoutHandler,Pe as redoclyLoginCallbackHandler,be as redoclyTokenLoginHandler,qe as samlCallbackHandler};
1
+ import{setCookie as R,deleteCookie as q}from"hono/cookie";import{AuthProviderType as X}from"@redocly/config";import{withPathPrefix as L,getPathPrefix as C}from"@redocly/theme/core/utils";import{compareURIs as W}from"../../../utils/url/compare-uris.js";import{ensureArray as b}from"../../../utils/array/ensure-array.js";import{ALTERNATIVE_AUD_CLAIM_NAME as F,JWT_SECRET_KEY as $,ORG_SLUG as Y,ORG_ID as Q}from"../../constants/common.js";import{DEFAULT_COOKIE_EXPIRATION as B,ServerRoutes as S}from"../../../constants/common.js";import{sanitizeRedirectPathname as z}from"../../../utils/url/sanitize-redirect-pathname.js";import{telemetry as M}from"../../telemetry/index.js";import{getAuthProviderLoginParams as Z,isOidcProviderConfig as U,isSaml2ProviderConfig as x,oidcExchangeCodeForToken as ee,buildLoginUrl as re,decodeSamlResponse as oe,extractUserClaims as ne,parseSamlResponse as te,parseOidcState as ie,verifySAMLResponse as se,getUsernameFromPayload as ae,buildOidcLogoutUrl as de,getOidcMetadata as H,getRedoclyTokenPayload as ce,isRedoclySso as le,rewritePreviewAuthRedirectUri as ue,parsePreviewBranch as N,buildOidcLoginUrl as pe,createMcpSessionResource as k}from"../auth.js";import*as P from"../jwt/jwt.js";import{AlgorithmTypes as v}from"../jwt/types.js";import{handleErrorPageRender as ge}from"../utils.js";import{encodeBase64URL as fe}from"../jwt/encode.js";async function Oe(i){if(process.env.NODE_ENV==="production")return i.newResponse(null,404,{});const{password:e,...r}=await i.req.json(),a=await P.sign({...r,name:r.username||r.email||"Unknown"},$,v.HS256);return R(i,"authorization",a,{path:C()||"/",httpOnly:!0,secure:!0,sameSite:"none"}),i.newResponse(null,200,{})}function Pe(){return async i=>{const e=i.get("logger"),r=encodeURIComponent(i.req.query("message")||"");e.error(`Login error: ${r}`);const a=`${S.LOGIN}/?error=${encodeURIComponent(r)}`;return i.newResponse(null,301,{Location:a})}}function j(i){if(!i||!i.includes(S.MCP_CALLBACK))return null;try{const e=i.split("/"),r=e[e.length-1];if(r){const a=Buffer.from(r,"base64url").toString("utf-8");return JSON.parse(a).mcpSessionId||null}}catch{}return null}function ve(i){return async e=>{const r=e.get("logger"),a=i.getConfig().ssoDirect,n=ie(e.req.query("state")),f=n.idpId,t=n.source==="mcp"||n.redirectTo&&typeof n.redirectTo=="string"&&n.redirectTo.includes(S.MCP_CALLBACK),c=t?j(typeof n.redirectTo=="string"?n.redirectTo:void 0):null,s=a?.[f];if(!U(s))return r.error("OIDC login error: missing OIDC provider config"),e.text("Forbidden",403);const d=await H(f,s);if(a&&!d.token_endpoint){const p="Invalid OIDC configuration: token_endpoint is required";return r.error(`OIDC login error: ${p}`),e.text(p,500)}try{const p=d.token_endpoint,l=e.req.query("code"),m=e.req.query("error");if(m)return t&&M.sendMcpAuthorizationFailedMessage([{...k(c),error:`OIDC error: ${m}`,error_details:e.req.query("error_description")||null}]),ge(e,i,{slug:"/"},403,"403OIDC");if(!l){const h="Code is expected but not present";return r.error(`OIDC login error: ${h}`),t&&M.sendMcpAuthorizationFailedMessage([{...k(c),error:h,error_details:null}]),new Response(`Forbidden: ${h}`,{status:403})}const w=e.req.header("x-forwarded-host"),g=e.req.header("x-forwarded-proto")||"https",A=t&&typeof n.redirectUri=="string"?n.redirectUri:new URL(L(S.OIDC_CALLBACK),w?`${g}://${w}`:e.req.url).toString(),_=e.get("cookies")?.code_verifier,u=await ee(p,l,A,s,{...s.tokenRequestCustomParams,..._?{code_verifier:_}:{}});if(u.error)return r.error(`Error from OIDC provider: "${u.error}"`),t&&M.sendMcpAuthorizationFailedMessage([{...k(c),error:`Token exchange error: ${u.error}`,error_details:u.error_description||null}]),e.text(`Forbidden: ${u.error_description||u.error}`,403);if(!u?.id_token){const h="No id_token, please, add openid to scopes";return r.error(`OIDC login error: ${h}`),t&&M.sendMcpAuthorizationFailedMessage([{...k(c),error:h,error_details:null}]),new Response(`Forbidden: ${h}`,{status:403})}const{payload:o,header:T}=P.decode(u.id_token),K=T.alg===v.RS256;if(s.audience?.length&&![...b(o.aud||[]),...b(o[F]||[])].some(I=>s.audience?.includes(I))){const I="No valid audience found in id_token";return r.error(`OIDC login error: ${I}`),t&&M.sendMcpAuthorizationFailedMessage([{...k(c),error:I,error_details:null}]),new Response(`Forbidden: ${I}`)}const E=K?u.id_token:await P.sign({...o,idpId:f},$,v.HS256);ae(o)||r.warn("To display your username, the required 'email' or 'full_profile' scope must be added to the identity provider configuration");const D=s?.tokenExpirationTime?Date.now()+s.tokenExpirationTime*1e3:o.exp*1e3||Date.now()+B*1e3;if(s.introspectEndpoint){const h=await fetch(s.introspectEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:u.access_token})});if(h.ok){const O=(await h.json()).ext?.federatedIdentity;O&&(R(e,"federated_access_token",O.access_token||"",{path:C()||"/",httpOnly:!1,expires:new Date(D)}),R(e,"federated_id_token",O.id_token||"",{path:C()||"/",httpOnly:!1,expires:new Date(D)}))}else r.warn(`OIDC introspect error: ${h.statusText}`)}if(R(e,"authorization",E,{path:C()||"/",httpOnly:!0,expires:new Date(D)}),E!==u.id_token&&R(e,"idp_id_token",u.id_token||"",{path:C()||"/",httpOnly:!0,expires:new Date(D)}),R(e,"idp_access_token",u.access_token||"",{path:C()||"/",httpOnly:!0,expires:new Date(D)}),q(e,"code_verifier",{path:C()||"/"}),t&&n.redirectTo&&typeof n.redirectTo=="string"&&n.redirectTo.includes(S.MCP_CALLBACK)){const h=e.req.url.split("?")[0].replace(S.OIDC_CALLBACK,""),I=L(n.redirectTo),O=`${h}${I}`;return e.newResponse(null,302,{Location:O})}const J=typeof n.redirectTo=="string"?n.redirectTo:void 0;let V=z(new URL(J||"/",e.req.url).pathname);const G=e.newResponse(null,302,{Location:V});return r.updateContext({email:o.email,subject:o.sub}),r.info("OIDC login successful"),G}catch(p){const l=p instanceof Error?p.message:String(p),m=p instanceof Error?p.stack:String(p);if(r.error(`OIDC login error: ${l}`),t&&M.sendMcpAuthorizationFailedMessage([{...k(c),error:l,error_details:m}]),p.error==="access_denied")return r.info("Access denied"),e.text("Forbidden",403)}const y="Something went wrong";return r.error(`OIDC login error: ${y}`),t&&M.sendMcpAuthorizationFailedMessage([{...k(c),error:y,error_details:null}]),e.text(y,500)}}function $e(i){return async e=>{const r=e.get("logger"),n=e.get("auth").claims?.idpId,t=i.getConfig().ssoDirect?.[n];if(e.req.method==="POST")return U(t)||q(e,"authorization",{path:C()||"/"}),r.info("Logout successful"),e.newResponse(null,200,{});let c;if(U(t)){const s=(await H(n,t)).end_session_endpoint;if(s){const d=new URL(e.req.url),y=e.req.header("x-forwarded-proto")||d.protocol.slice(0,-1)||"https",p=e.req.header("x-forwarded-host")||d.host,l=`${y}://${p}`,m=N(l),w=m?fe(JSON.stringify({branch:N(l)})):void 0,g=m?`${ue(l)}/_auth/logout`:`${l}/post-logout`;c=de(s,g,e.get("cookies")?.idp_id_token||e.get("cookies")?.authorization||"",w)}}return r.info("Logout successful"),q(e,"authorization",{path:C()||"/"}),e.newResponse(null,302,{Location:c||L("/")})}}function Ue(i){return async e=>{const r=i.getConfig().logoutReturnUrl,a=r||L("/");return e.newResponse(null,302,{Location:a})}}function Te(i){return async e=>{const r=e.get("logger"),a=e.req.param("code"),n=process.env.BH_API_URL,f=(t,c,s)=>t&&c?`${t} ${c.charAt(0)}`:s;try{if(!n)throw new Error("BH_API_URL is not set");const t=i.getConfig().ssoDirect;if(!t||!Object.keys(t).length)return r.warn("Invite no sso configured to handle"),e.redirect(L("/"));const c=await fetch(`${n}/user-invites/public/${a}`);if(!c.ok)return c.status===404?(r.warn(`Invite ${a} not found redirect to homepage`),e.redirect(L("/"))):(r.error("Invite error",await c.text()),e.redirect(L("/")));const s=await c.json(),d=new URL(L("/invite"),e.req.url);return d.searchParams.set("code",a),d.searchParams.set("org",s.organization.name),d.searchParams.set("invitedBy",f(s.invitedBy.firstName,s.invitedBy.lastName,s.invitedBy.name)),e.newResponse(null,302,{Location:d.toString()})}catch(t){return r.error("Error processing invite",{error:t,inviteCode:a}),e.text(t.message||"Failed to process invite",400)}}}function qe(i){return async e=>{const r=e.get("logger"),a=i.getConfig().ssoDirect,n=new URL(e.req.url),f=e.req.query("inviteCode"),t=e.req.header("x-forwarded-proto")||n.protocol.slice(0,-1)||"https",c=e.req.header("x-forwarded-host")||n.host,s=`${t}://${c}`;let d=n.searchParams.get("idpId");const y=n.searchParams.get("redirectTo"),p=Object.keys(a||{})[0];d=d||p;const l=n.searchParams.get("mcp_redirect_uri"),m=!!l;if(!a?.[d]){const o="Invalid idpId";if(r.error(`IdP login error: ${o}`),m){const T=j(y||void 0);M.sendMcpAuthorizationFailedMessage([{...k(T),error:o,error_details:null}])}return e.text(`Forbidden: ${o}`,403)}const g=d&&a?await Z(d,a[d]):void 0,A={};for(const o of Object.keys(g?.extraParams||{}))A[o]=n.searchParams.get(o)||g?.extraParams?.[o]||void 0;let _,u={};if(m&&l&&g&&g.type===X.OIDC){r.info(`Building MCP OAuth login URL with redirect_uri: ${l}`);const o=pe("",{...g,extraParams:A},y,f,{redirectUriOverride:l,sourceOverride:"mcp",branchOverride:void 0});_=o.loginUrl,u=o.cookies||{}}else if(g){const o=re({...g,extraParams:A},s,y,f);_=o.loginUrl,u=o.cookies||{}}return Object.keys(u).forEach(o=>{R(e,o,u[o].value,u[o].options)}),r.info(`IdP login initiated for ID '${d}'`),e.newResponse(null,302,{Location:_||new URL(e.req.url).pathname})}}function be(i){return async e=>{const r=e.get("logger"),a=await e.req.formData(),n=a.get("SAMLResponse"),f=a.get("RelayState");if(typeof n!="string"||typeof f!="string"){const o="SAMLResponse is required";return r.error(`SAML2 login error: ${o}`),e.text(`Bad request: ${o}`,400)}const t=oe(n),{success:c,uid:s,nameFormat:d,attrs:y,issuerId:p,expiresAt:l}=te(t),{idpId:m,redirectTo:w}=JSON.parse(f);if(!c){const o="SAML2 assertion is not successful";return r.error(`SAML2 login error: ${o}`),e.text(`Permission denied: ${o}`,401)}if(!l||Math.ceil(Date.now()/1e3)>=l){const o="SAML2 Token Expired";return r.error(`SAML2 login error: ${o}`),e.text(o,401)}const g=i.getConfig().ssoDirect?.[m];if(!g||!x(g)){const o="Cannot find valid IdP";return r.error(`SAML2 login error: ${o}`),e.text(`Permission denied: ${o}`,401)}if(!(g.issuerId&&p&&W(g.issuerId,p))){const o="IssuerID is misconfigured or untrusted assertions issuer received";return r.error(`SAML2 login error: ${o}`),e.text(`Permission denied: ${o}`,401)}if(!await se(t,g.x509PublicCert)){const o="SAMLResponse signature invalid";return r.error(`SAML2 login error: ${o}`),e.text(o,401)}const _=ne(s,d,y,g.teamsAttributeName);if(!_.sub){const o="The provider did not return a valid user identity.";return r.error(`SAML2 login error: ${o}`),e.text(o,400)}if(!_.email){const o="The provider did not return a valid user email.";return r.error(`SAML2 login error: ${o}`),e.text(o,400)}const u=await P.sign({..._,idpId:m},$,v.HS256);return R(e,"authorization",u,{path:C()||"/",httpOnly:!0,expires:new Date(l*1e3)}),r.updateContext({email:_.email,subject:_.sub}),r.info("SAML2 login successful"),e.newResponse(null,302,{Location:w||"/"})}}function Ee(i){return async e=>{const r=e.get("logger"),a=new URL(e.req.query("redirectTo")||"/",e.req.url),n=L(z(a.pathname)),f=i.getConfig().ssoDirect,t=Object.entries(f||{}).find(([,w])=>U(w)&&le(w));if(!(f&&t))return e.newResponse(null,302,{Location:n});const s=e.req.query("token"),d=s&&await ce(s);if(!d)return e.newResponse(null,302,{Location:n});if(!b(d[F]||[]).some(w=>w===Y||w===Q))return e.newResponse(null,302,{Location:n});const l=await P.sign({...d,idpId:t?.at(0)},$,v.HS256),m=Date.now()+B*1e3;return R(e,"authorization",l,{path:C()||"/",httpOnly:!0,expires:new Date(m),sameSite:"None",secure:!0}),r.info("Token login successful"),e.newResponse(null,302,{Location:n})}}export{Oe as authorizeHandler,qe as idpLoginHandler,Te as inviteHandler,$e as logoutHandler,ve as oidcCallbackHandler,Ue as postLogoutHandler,Pe as redoclyLoginCallbackHandler,Ee as redoclyTokenLoginHandler,be as samlCallbackHandler};
@@ -1 +1 @@
1
- import{telemetryTraceStep as o}from"../../../telemetry/helpers/trace-step.js";import{CATALOG_ENTITY_KEY as u}from"../../../../constants/common.js";import{ALLOWED_CATALOG_QUERY_PARAMS as l}from"../../../constants/plugins/catalog-entities.js";import{allowlistObject as d}from"../../../../utils/object/allowlist-object.js";import{CatalogEntitiesService as f}from"../../../plugins/catalog-entities/database/catalog-entities-service.js";import{createPaginationParamsValidator as g}from"../../../providers/database/pagination/schemas.js";import{isValidIsoDate as c}from"../../../utils/is-valid-iso-date.js";import{isValidSanitizedString as v}from"../../../utils/validate-and-sanitize-string.js";const E=["type","key","title","summary","tags","metadata","metadata.*","git","contact","links","id","source","sourceFile","version","revision","createdAt","updatedAt","domains","owners"],p=async({catalogEntitiesService:n,ctx:t})=>o("catalog_entities.bff.get_entities",async e=>{const r=t.req.query();e?.setAttribute("queryParams",JSON.stringify(d(r,l)));const i=g(E).parse(r),a=await n.getEntitiesWithRelations(i);return e?.setAttribute("entitiesCount",a.items.length),t.json(a)}),A=async({catalogEntitiesService:n,ctx:t})=>o("catalog_entities.bff.get_entity",async e=>{const r=t.req.param(u);if(!r)return e?.error(new Error("Entity key is required")),t.json({message:"Entity key is required"},400);const i=t.req.query("revision");let a=null;if(i){if(!c(i))return e?.error(new Error("Invalid revision parameter")),t.json({message:"Invalid revision parameter: must be a valid ISO 8601 date-time string"},400);a=i}const m=t.req.query("version");if(!v(m,{pattern:/^[a-zA-Z0-9._-]+$/,maxLength:100,allowEmpty:!0}))return e?.error(new Error("Invalid version parameter")),t.json({message:"Invalid version parameter: version must contain only alphanumeric characters, dots, hyphens, and underscores, and must not exceed 100 characters"},400);const y=t.req.query();e?.setAttribute("queryParams",JSON.stringify(y)),e?.setAttribute("pathParams",JSON.stringify({entityKey:r}));const s=await n.getEntityWithRelationsByKey(r,{revision:a,version:m});return s?(e?.setAttribute("entity",JSON.stringify(s)),t.json(s)):(e?.error(new Error("Entity not found")),t.json({message:"Entity not found"},404))});function j(n){return async t=>o("catalog_entities.bff",async e=>{e?.setAttribute("method",t.req.method);const r=await f.getInstance({baseDbDir:n.serverOutDir});return t.req.param("entityKey")?A({catalogEntitiesService:r,ctx:t}):p({catalogEntitiesService:r,ctx:t})})}export{j as bffCatalogHandler};
1
+ import{expandTeamsForRead as u}from"../../../utils/rbac.js";import{telemetryTraceStep as y}from"../../../telemetry/helpers/trace-step.js";import{CATALOG_ENTITY_KEY as d}from"../../../../constants/common.js";import{ALLOWED_CATALOG_QUERY_PARAMS as f}from"../../../constants/plugins/catalog-entities.js";import{allowlistObject as c}from"../../../../utils/object/allowlist-object.js";import{CatalogEntitiesService as p}from"../../../plugins/catalog-entities/database/catalog-entities-service.js";import{createPaginationParamsValidator as v}from"../../../providers/database/pagination/schemas.js";import{isValidIsoDate as b}from"../../../utils/is-valid-iso-date.js";import{isValidSanitizedString as E}from"../../../utils/validate-and-sanitize-string.js";const A=["type","key","title","summary","tags","metadata","metadata.*","git","contact","links","id","source","sourceFile","version","revision","createdAt","updatedAt","domains","owners"],q=async({catalogEntitiesService:r,ctx:t,store:n})=>y("catalog_entities.bff.get_entities",async e=>{const i=t.req.query();e?.setAttribute("queryParams",JSON.stringify(c(i,f)));const a=v(A).parse(i),o=u(n.getConfig().rbac||{},t.get("auth").teams),s=await r.getEntitiesWithRelations(a,o);return e?.setAttribute("entitiesCount",s.items.length),t.json(s)}),h=async({catalogEntitiesService:r,ctx:t,store:n})=>y("catalog_entities.bff.get_entity",async e=>{const i=t.req.param(d);if(!i)return e?.error(new Error("Entity key is required")),t.json({message:"Entity key is required"},400);const a=t.req.query("revision");let o=null;if(a){if(!b(a))return e?.error(new Error("Invalid revision parameter")),t.json({message:"Invalid revision parameter: must be a valid ISO 8601 date-time string"},400);o=a}const s=t.req.query("version");if(!E(s,{pattern:/^[a-zA-Z0-9._-]+$/,maxLength:100,allowEmpty:!0}))return e?.error(new Error("Invalid version parameter")),t.json({message:"Invalid version parameter: version must contain only alphanumeric characters, dots, hyphens, and underscores, and must not exceed 100 characters"},400);const g=t.req.query();e?.setAttribute("queryParams",JSON.stringify(g)),e?.setAttribute("pathParams",JSON.stringify({entityKey:i}));const l=u(n.getConfig().rbac||{},t.get("auth").teams),m=await r.getEntityWithRelationsByKey(i,{revision:o,version:s},l);return m?(e?.setAttribute("entity",JSON.stringify(m)),t.json(m)):(e?.error(new Error("Entity not found")),t.json({message:"Entity not found"},404))});function R(r){return async t=>y("catalog_entities.bff",async n=>{n?.setAttribute("method",t.req.method);const e=await p.getInstance({baseDbDir:r.serverOutDir});return t.req.param("entityKey")?h({catalogEntitiesService:e,ctx:t,store:r}):q({catalogEntitiesService:e,ctx:t,store:r})})}export{R as bffCatalogHandler};
@@ -1 +1 @@
1
- import{getCompleteCatalogConfig as c}from"../../../plugins/catalog-entities/get-complete-catalog-config.js";import{parseAndValidateEntities as E,parseAndValidateEntity as f}from"../../../plugins/catalog-entities/entities/validate-entity.js";import{telemetryTraceStep as g}from"../../../telemetry/helpers/trace-step.js";import{CATALOG_FILTERS_CACHE_NAMESPACE as A,ALLOWED_CATALOG_QUERY_PARAMS as w}from"../../../constants/plugins/catalog-entities.js";import{CATALOG_ENTITY_KEY as u}from"../../../../constants/common.js";import{allowlistObject as b}from"../../../../utils/object/allowlist-object.js";import{CatalogEntitiesService as h}from"../../../plugins/catalog-entities/database/catalog-entities-service.js";import{createPaginationParamsValidator as j}from"../../../providers/database/pagination/schemas.js";import{createEntityRelationDtoFromFileSchema as _}from"../../../plugins/catalog-entities/database/mappers/create-entity-relation-dto-from-file-schema.js";import{CacheService as q}from"../../../persistence/cache/services/cache-service.js";const p=["type","key","title","summary","tags","metadata","metadata.*","git","contact","links","id","source","sourceFile","createdAt","updatedAt"],m=async s=>{await(await q.getInstance({baseDbDir:s})).deleteByNamespace(A)},O=async({catalogEntitiesService:s,ctx:e})=>g("catalog_entities.get_entities",async i=>{const n=e.get("logger");try{const r=e.req.query();i?.setAttribute("queryParams",JSON.stringify(b(r,w)));const o=j(p).parse(r),t=await s.getEntities(o);return i?.setAttribute("entitiesCount",t.items.length),e.json(t)}catch(r){return n.error(r),i?.error(r),e.json({message:"Failed to get entities"},500)}}),C=async({catalogEntitiesService:s,ctx:e})=>g("catalog_entities.get_entity",async i=>{const n=e.req.param(u);if(!n)return i?.error(new Error("Entity key is required")),e.json({message:"Entity key is required"},400);i?.setAttribute("pathParams",JSON.stringify({entityKey:n}));const r=await s.getEntityByKey(n);return r?(i?.setAttribute("entity",JSON.stringify(r)),e.json(r)):(i?.error(new Error("Entity not found")),e.json({message:"Entity not found"},404))}),F=async({catalogEntitiesService:s,ctx:e,catalogConfig:i,serverOutDir:n})=>g("catalog_entities.create_entity",async r=>{const o=e.get("logger");try{const t=await e.req.json();r?.setAttribute("requestBody",JSON.stringify(t));const a=f(t,i),y=await s.createEntity(a),l=a.relations?.map(d=>_(a.key,d));return await s.createEntitiesRelations(l??[]),y?(m(n),r?.setAttribute("entity",JSON.stringify(y)),e.json(y)):(r?.error(new Error("Failed to create entity")),e.json({message:"Failed to create entity"},500))}catch(t){return o.error(t),t instanceof Error&&t.message.includes("validation failed")?(r?.error(new Error(t.message)),e.json({message:t.message},400)):(r?.error(t),e.json({message:"Failed to create entity"},500))}}),P=async({catalogEntitiesService:s,ctx:e,catalogConfig:i,serverOutDir:n})=>g("catalog_entities.bulk_upsert_entities",async r=>{const o=e.get("logger");try{const t=await e.req.json();r?.setAttribute("requestBody",JSON.stringify(t));const a=E(t,i),y=await s.createEntities(a);if(!y.length)return r?.error(new Error("Failed to create entities")),e.json({message:"Failed to create entities"},500);m(n);const l=y.filter(d=>d.status==="ok");return r?.setAttribute("totalSuccess",l.length),r?.setAttribute("totalFailed",y.length-l.length),e.json(y,207)}catch(t){return o.error(t),t instanceof Error&&t.message.includes("validation failed")?(r?.error(new Error(t.message)),e.json({message:t.message},400)):(r?.error(t),e.json({message:"Failed to create entities"},500))}}),N=async({catalogEntitiesService:s,ctx:e,serverOutDir:i})=>g("catalog_entities.update_entity",async n=>{const r=e.get("logger"),o=e.req.param(u);if(!o)return n?.error(new Error("Entity key is required")),e.json({message:"Entity key is required"},400);n?.setAttribute("pathParams",JSON.stringify({entityKey:o}));try{const t=await e.req.json();n?.setAttribute("requestBody",JSON.stringify(t));const a=await s.updateEntityByKey(o,t);return a?(m(i),n?.setAttribute("entity",JSON.stringify(a)),e.json(a)):(n?.error(new Error("Failed to update entity")),e.json({message:"Failed to update entity"},500))}catch(t){return r.error(t),t instanceof Error&&t.message.includes("validation failed")?(n?.error(new Error(t.message)),e.json({message:t.message},400)):(n?.error(t),e.json({message:t.message},500))}}),T=async({catalogEntitiesService:s,ctx:e,serverOutDir:i})=>g("catalog_entities.delete_entity",async n=>{const r=e.req.param(u);return r?(n?.setAttribute("pathParams",JSON.stringify({entityKey:r})),await s.deleteEntityByKey(r),m(i),new Response(null,{status:204})):(n?.error(new Error("Entity key is required")),e.json({message:"Entity key is required"},400))}),k={GET:O,POST:F,PUT:P},v={GET:C,DELETE:T,PATCH:N};function U(s){return async e=>g("catalog_entities",async i=>{const n=await h.getInstance({baseDbDir:s.serverOutDir}),r=e.req.method;i?.setAttribute("method",r);const o=e.req.param(u)?v:k,t=c(s.config.entitiesCatalog),a=o[r];return a?await a({catalogEntitiesService:n,ctx:e,catalogConfig:t,serverOutDir:s.serverOutDir}):(i?.error(new Error("Method not allowed")),e.json({message:"Method not allowed"},405))})}export{U as catalogHandler};
1
+ import{getCompleteCatalogConfig as c}from"../../../plugins/catalog-entities/get-complete-catalog-config.js";import{parseAndValidateEntities as E,parseAndValidateEntity as f,parseAndValidateEntityUpdate as w}from"../../../plugins/catalog-entities/entities/validate-entity.js";import{telemetryTraceStep as g}from"../../../telemetry/helpers/trace-step.js";import{CATALOG_FILTERS_CACHE_NAMESPACE as A,ALLOWED_CATALOG_QUERY_PARAMS as h}from"../../../constants/plugins/catalog-entities.js";import{CATALOG_ENTITY_ID as u}from"../../../../constants/common.js";import{allowlistObject as p}from"../../../../utils/object/allowlist-object.js";import{isValidIsoDate as b}from"../../../utils/is-valid-iso-date.js";import{CatalogEntitiesService as j}from"../../../plugins/catalog-entities/database/catalog-entities-service.js";import{createPaginationParamsValidator as _}from"../../../providers/database/pagination/schemas.js";import{createEntityRelationDtoFromFileSchema as q}from"../../../plugins/catalog-entities/database/mappers/create-entity-relation-dto-from-file-schema.js";import{CacheService as O}from"../../../persistence/cache/services/cache-service.js";const C=["type","key","title","summary","tags","metadata","metadata.*","git","contact","links","id","source","sourceFile","createdAt","updatedAt"],m=async n=>{await(await O.getInstance({baseDbDir:n})).deleteByNamespace(A)},F=async({catalogEntitiesService:n,ctx:t})=>g("catalog_entities.get_entities",async s=>{const a=t.get("logger");try{const e=t.req.query();s?.setAttribute("queryParams",JSON.stringify(p(e,h)));const d=_(C).parse(e),r=await n.getEntities(d);return s?.setAttribute("entitiesCount",r.items.length),t.json(r)}catch(e){return a.error(e),s?.error(e),t.json({message:"Failed to get entities"},500)}}),v=async({catalogEntitiesService:n,ctx:t})=>g("catalog_entities.get_entity",async s=>{const a=t.req.param(u);if(!a)return s?.error(new Error("Entity id is required")),t.json({message:"Entity id is required"},400);s?.setAttribute("pathParams",JSON.stringify({entityId:a}));const e=await n.getEntityById(a);return e?(s?.setAttribute("entity",JSON.stringify(e)),t.json(e)):(s?.error(new Error("Entity not found")),t.json({message:"Entity not found"},404))}),D=async({catalogEntitiesService:n,ctx:t,catalogConfig:s,serverOutDir:a})=>g("catalog_entities.create_entity",async e=>{const d=t.get("logger");try{const r=await t.req.json();e?.setAttribute("requestBody",JSON.stringify(r));const i=f(r,s),o=await n.createEntity(i),y=i.relations?.map(l=>q(i.key,l));return await n.createEntitiesRelations(y??[]),o?(m(a),e?.setAttribute("entity",JSON.stringify(o)),t.json(o)):(e?.error(new Error("Failed to create entity")),t.json({message:"Failed to create entity"},500))}catch(r){return d.error(r),r instanceof Error&&r.message.includes("validation failed")?(e?.error(new Error(r.message)),t.json({message:r.message},400)):(e?.error(r),t.json({message:"Failed to create entity"},500))}}),P=async({catalogEntitiesService:n,ctx:t,catalogConfig:s,serverOutDir:a})=>g("catalog_entities.bulk_upsert_entities",async e=>{const d=t.get("logger");try{const r=await t.req.json();e?.setAttribute("requestBody",JSON.stringify(r));const i=E(r,s),o=await n.createEntities(i);if(!o.length)return e?.error(new Error("Failed to create entities")),t.json({message:"Failed to create entities"},500);m(a);const y=o.filter(l=>l.status==="ok");return e?.setAttribute("totalSuccess",y.length),e?.setAttribute("totalFailed",o.length-y.length),t.json(o,207)}catch(r){return d.error(r),r instanceof Error&&r.message.includes("validation failed")?(e?.error(new Error(r.message)),t.json({message:r.message},400)):(e?.error(r),t.json({message:"Failed to create entities"},500))}}),T=async({catalogEntitiesService:n,ctx:t,catalogConfig:s,serverOutDir:a})=>g("catalog_entities.update_entity",async e=>{const d=t.get("logger"),r=t.req.param(u);if(!r)return e?.error(new Error("Entity id is required")),t.json({message:"Entity id is required"},400);e?.setAttribute("pathParams",JSON.stringify({entityId:r}));try{const i=await t.req.json();e?.setAttribute("requestBody",JSON.stringify(i));const o=await n.getEntityById(r);if(!o)return e?.error(new Error(`Entity with id: ${r} not found`)),t.json({message:`Entity with id: ${r} not found`},404);const y=w(i,s,i?.type??o.type);if(y.revision&&!b(y.revision))throw new Error("Entity validation failed: 'entity.revision' must be a valid ISO 8601 date-time string");const l=await n.updateEntity(y,o);return l?(m(a),e?.setAttribute("entity",JSON.stringify(l)),t.json(l)):(e?.error(new Error("Failed to update entity")),t.json({message:"Failed to update entity"},500))}catch(i){return d.error(i),i instanceof Error&&i.message.includes("validation failed")?(e?.error(new Error(i.message)),t.json({message:i.message},400)):(e?.error(i),t.json({message:i.message},500))}}),N=async({catalogEntitiesService:n,ctx:t,serverOutDir:s})=>g("catalog_entities.delete_entity",async a=>{const e=t.req.param(u);return e?(a?.setAttribute("pathParams",JSON.stringify({entityId:e})),await n.deleteEntity(e),m(s),new Response(null,{status:204})):(a?.error(new Error("Entity id is required")),t.json({message:"Entity id is required"},400))}),I={GET:F,POST:D,PUT:P},J={GET:v,DELETE:N,PATCH:T};function Q(n){return async t=>g("catalog_entities",async s=>{const a=await j.getInstance({baseDbDir:n.serverOutDir}),e=t.req.method;s?.setAttribute("method",e);const d=t.req.param(u)?J:I,r=c(n.config.entitiesCatalog),i=d[e];return i?await i({catalogEntitiesService:a,ctx:t,catalogConfig:r,serverOutDir:n.serverOutDir}):(s?.error(new Error("Method not allowed")),t.json({message:"Method not allowed"},405))})}export{Q as catalogHandler};
@@ -1 +1 @@
1
- import u from"path";import{REDOCLY_ROUTE_RBAC as E,REDOCLY_TEAMS_RBAC as w}from"@redocly/config";import{withoutPathPrefix as M,withPathPrefix as q}from"@redocly/theme/core/utils";import{CACHE_CONTROL_NO_CACHE_HEADER_VALUE as _,DEFAULT_IMMUTABLE_CACHE_MAX_AGE as O}from"../../constants/common.js";import{removeTrailingSlash as F}from"../../../utils/url/remove-trailing-slash.js";import{findInIterable as H}from"../../../utils/collection/find-in-iterable.js";import{sanitizeRedirectPathname as v}from"../../../utils/url/sanitize-redirect-pathname.js";import{sanitizePath as z}from"../../../utils/path/sanitize-path.js";import{normalizeRouteSlug as B}from"../../../utils/path/normalize-route-slug.js";import{isPathInFolder as G}from"../../../utils/path/is-path-in-folder.js";import{processRedirects as N}from"./helpers/process-redirects.js";import{renderPage as Y,getServerProps as $}from"../../ssr/index.js";import{canAccessAsset as k,canAccessResource as L}from"../../utils/rbac.js";import{handleErrorPageRender as y,handleUnauthorized as P,handleUnauthorizedAsset as U}from"../utils.js";import{DEFAULT_MAX_AGE_FOR_MIME_TYPE as V,MIME_TYPES as W}from"../mime-types.js";import{fileExistsAsync as X}from"../../utils/index.js";import{getRedirectRoute as Z}from"../utils/legacy-openapi-redirects.js";import{telemetry as j}from"../../../cli/telemetry/index.js";import{telemetry as J}from"../../telemetry/index.js";function Ae(e,I,T){return async o=>{const h=o.get("logger"),l=o.req,i=new URL(l.url),n=M(z(decodeURIComponent(i.pathname))),R=u.parse(n).ext===".md",m=B(n),r=(l.method==="GET"||l.method==="HEAD")&&!R?e.getRouteBySlug(m,{followRedirect:!1})||H(e.routesBySlug?.values(),t=>t.hasClientRoutes&&(n===t.slug||n.startsWith(t.slug+"/"))):void 0,A=e.getRedirect(m);if(A){const t=N({redirect:A,reqUrlSearch:i.search});return J.sendRedirectMessage({from:m,templateId:t.type.toString()}),o.newResponse(null,t.type,{Location:t.location})}const d=process.env.NODE_ENV==="production"?301:302;if(r?.metadata?.type==="openapi"){const t=Z(i.pathname);if(t)return h.info("Legacy OpenAPI docs redirect from "+i.pathname),o.newResponse(null,d,{Location:encodeURI(t)});if(i.pathname.match(/[A-Z]/))return h.warn("Redirect to lowercase route to avoid 404 error"),o.newResponse(null,d,{Location:encodeURI(i.pathname.toLowerCase())})}if(n.endsWith("/")&&n!=="/"){const t=v(new URL(m||"/",o.req.url).pathname);return o.newResponse(null,d,{Location:encodeURI(q((t==="/"?"/":F(t))+i.search))})}const{isAuthenticated:c,teams:f,claims:{email:p}}=o.get("auth");if(r){if(!L(r,{isAuthenticated:c,email:p,teams:f},e.config.rbac,e.config.requiresLogin))return c?y(o,e,{slug:r.slug,[w]:r[w],[E]:r[E]},403):P(o,e,r.slug);const t=await I(r),s=await $(r,o,t,e),{html:b,statusCode:D}=await Y(r,s,o,e,j);return o.html(b,D,{"Cache-Control":_})}const a=u.resolve(e.outdir,"."+n);if(!G(a,e.outdir))return U(o);if(R){const t=n==="index.html.md"?"/":n.replace(/\.md$/,""),s=e.getRouteBySlug(t,{followRedirect:!1});if(s&&!L(s,{isAuthenticated:c,email:p,teams:f},e.config.rbac,e.config.requiresLogin))return P(o,e,n)}if(!k(n,e.getGlobalConfig("rbac"),e.getGlobalConfig("requiresLogin"),e.getGlobalConfig("directoryPaths"),{isAuthenticated:c,email:p,teams:f}))return U(o);const C=W[u.extname(a)]||"text/plain",g=a.match(/assets\/.*\.[a-f0-9]{8,}\..+/)||a.match(/runtime\/chunks\/.*/)?O:V[C],S=g?{"Cache-Control":`public, max-age=${g}, immutable`,Expires:new Date(Date.now()+g*1e3).toUTCString()}:{"Cache-Control":_};if(await X(a)){const t=l.query("download")!=null,s=await T(a);return o.newResponse(s,200,{"Content-Type":C,"Access-Control-Allow-Origin":"*",...S,...t&&{"Content-Disposition":`attachment; filename="${u.basename(a)}"`}})}else return y(o,e,{slug:m},404)}}export{Ae as dynamicRouteHandler};
1
+ import u from"path";import{REDOCLY_ROUTE_RBAC as E,REDOCLY_TEAMS_RBAC as w}from"@redocly/config";import{withoutPathPrefix as M,withPathPrefix as q}from"@redocly/theme/core/utils";import{CACHE_CONTROL_NO_CACHE_HEADER_VALUE as _,DEFAULT_IMMUTABLE_CACHE_MAX_AGE as O}from"../../constants/common.js";import{removeTrailingSlash as F}from"../../../utils/url/remove-trailing-slash.js";import{findInIterable as H}from"../../../utils/collection/find-in-iterable.js";import{sanitizeRedirectPathname as v}from"../../../utils/url/sanitize-redirect-pathname.js";import{sanitizePath as z}from"../../../utils/path/sanitize-path.js";import{normalizeRouteSlug as B}from"../../../utils/path/normalize-route-slug.js";import{isPathInFolder as G}from"../../../utils/path/is-path-in-folder.js";import{processRedirects as N}from"./helpers/process-redirects.js";import{renderPage as Y,getServerProps as $}from"../../ssr/index.js";import{canAccessAsset as k,canAccessResource as L}from"../../utils/rbac.js";import{handleErrorPageRender as y,handleUnauthorized as P,handleUnauthorizedAsset as U}from"../utils.js";import{DEFAULT_MAX_AGE_FOR_MIME_TYPE as V,MIME_TYPES as W}from"../mime-types.js";import{fileExistsAsync as X}from"../../utils/index.js";import{getRedirectRoute as j}from"../utils/legacy-openapi-redirects.js";import{telemetry as Z}from"../../../cli/telemetry/index.js";import{telemetry as J}from"../../telemetry/index.js";function Ae(e,I,T){return async o=>{const h=o.get("logger"),c=o.req,i=new URL(c.url),n=M(z(decodeURIComponent(i.pathname))),R=u.parse(n).ext===".md",m=B(n),r=(c.method==="GET"||c.method==="HEAD")&&!R?e.getRouteBySlug(m,{followRedirect:!1})||H(e.routesBySlug?.values(),t=>t.hasClientRoutes&&(n===t.slug||n.startsWith(t.slug+"/"))):void 0,A=e.getRedirect(m);if(A){const t=N({redirect:A,reqUrlSearch:i.search});return J.sendRedirectMessage([{object:"redirect",from:m,templateId:t.type.toString()}]),o.newResponse(null,t.type,{Location:t.location})}const d=process.env.NODE_ENV==="production"?301:302;if(r?.metadata?.type==="openapi"){const t=j(i.pathname);if(t)return h.info("Legacy OpenAPI docs redirect from "+i.pathname),o.newResponse(null,d,{Location:encodeURI(t)});if(i.pathname.match(/[A-Z]/))return h.warn("Redirect to lowercase route to avoid 404 error"),o.newResponse(null,d,{Location:encodeURI(i.pathname.toLowerCase())})}if(n.endsWith("/")&&n!=="/"){const t=v(new URL(m||"/",o.req.url).pathname);return o.newResponse(null,d,{Location:encodeURI(q((t==="/"?"/":F(t))+i.search))})}const{isAuthenticated:l,teams:f,claims:{email:p}}=o.get("auth");if(r){if(!L(r,{isAuthenticated:l,email:p,teams:f},e.config.rbac,e.config.requiresLogin))return l?y(o,e,{slug:r.slug,[w]:r[w],[E]:r[E]},403):P(o,e,r.slug);const t=await I(r),s=await $(r,o,t,e),{html:b,statusCode:D}=await Y(r,s,o,e,Z);return o.html(b,D,{"Cache-Control":_})}const a=u.resolve(e.outdir,"."+n);if(!G(a,e.outdir))return U(o);if(R){const t=n==="index.html.md"?"/":n.replace(/\.md$/,""),s=e.getRouteBySlug(t,{followRedirect:!1});if(s&&!L(s,{isAuthenticated:l,email:p,teams:f},e.config.rbac,e.config.requiresLogin))return P(o,e,n)}if(!k(n,e.getGlobalConfig("rbac"),e.getGlobalConfig("requiresLogin"),e.getGlobalConfig("directoryPaths"),{isAuthenticated:l,email:p,teams:f}))return U(o);const C=W[u.extname(a)]||"text/plain",g=a.match(/assets\/.*\.[a-f0-9]{8,}\..+/)||a.match(/runtime\/chunks\/.*/)?O:V[C],S=g?{"Cache-Control":`public, max-age=${g}, immutable`,Expires:new Date(Date.now()+g*1e3).toUTCString()}:{"Cache-Control":_};if(await X(a)){const t=c.query("download")!=null,s=await T(a);return o.newResponse(s,200,{"Content-Type":C,"Access-Control-Allow-Origin":"*",...S,...t&&{"Content-Disposition":`attachment; filename="${u.basename(a)}"`}})}else return y(o,e,{slug:m},404)}}export{Ae as dynamicRouteHandler};
@@ -1 +1 @@
1
- import{getCookie as C}from"hono/cookie";import{ulid as w}from"ulid";import{AUTH_URL as _,JWT_SECRET_KEY as y}from"../../constants/common.js";import{ServerRoutes as d}from"../../../constants/common.js";import{withPathPrefix as m}from"@redocly/theme/core/utils";import{telemetry as l}from"../../telemetry/index.js";import{createMcpAuthorizationCode as k,verifyMcpAuthorizationCode as S,createMcpSessionResource as u}from"../auth.js";import*as h from"../jwt/jwt.js";import{getRequestOrigin as g}from"../utils/get-request-origin.js";const n=(e,r,o=200,a)=>e.json(r,o,{"Content-Type":"application/json",...a??{}});async function T(e){const r=Math.floor(Date.now()/1e3);return h.sign({type:"mcp_context",...e,iat:r,exp:r+600},y)}async function I(e){await h.verify(e,y);const{payload:r}=h.decode(e);if(r.type!=="mcp_context")throw new Error("Invalid context token type");return r}function O(){return async e=>{if(e.req.method!=="GET")return n(e,{error:"Method not allowed"},405,{Allow:"GET"});const r=g(e);return n(e,{resource:`${r}/mcp`,authorization_servers:[r],bearer_methods_supported:["header"],resource_documentation:`${r}/.well-known/oauth-authorization-server`,scopes_supported:["openid","profile","email","offline_access"],bearer_token_types_supported:["Bearer"]})}}function D(){return async e=>{const r=g(e);return n(e,{issuer:_||"",authorization_endpoint:`${r}${d.MCP_AUTHORIZATION}`,token_endpoint:`${r}${d.MCP_TOKEN_PORTAL}`,jwks_uri:`${_||""}/.well-known/jwks.json`,registration_endpoint:`${r}${d.MCP_DYNAMIC_CLIENT_REGISTRATION}`,scopes_supported:["openid","profile","email","offline_access"],response_types_supported:["code"],grant_types_supported:["authorization_code","refresh_token","client_credentials"],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"]})}}function $(){return async e=>{if(e.req.method!=="POST")return n(e,{error:"Method not allowed"},405);try{return n(e,{client_id:process.env.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:["authorization_code","refresh_token"],response_types:["code"],scope:"openid offline email",subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201)}catch(r){return n(e,{error:"invalid_request",error_description:r?.message||"Unable to register client"},500)}}}function q(){return async e=>{const r=new URL(e.req.url),{searchParams:o}=r,a=o.get("redirect_uri"),t=w();l.sendMcpAuthorizationStartedMessage({...u(t),redirect_uri:a||null});const s=g(e),c={isMcpFlow:!0,originalRedirectUri:a,mcpClientId:o.get("client_id"),mcpState:o.get("state"),mcpSessionId:t,timestamp:Date.now()};try{const i=await T(c),p=new URL(d.IDP_LOGIN,s);return p.searchParams.set("redirectTo",`${m(d.MCP_CALLBACK)}/${i}`),p.searchParams.set("idpId","oidc"),e.redirect(p.toString())}catch(i){const p=i instanceof Error?i.message:String(i),M=i instanceof Error?i.stack:String(i);l.sendMcpAuthorizationFailedMessage({...u(t),error:p,error_details:M});const f=new URL(m(`${_}/oauth2/auth`));return f.search=o.toString(),e.redirect(f.toString())}}}function H(){return async e=>{if(e.req.method!=="POST")return n(e,{error:"Method not allowed"},405);try{const r=await e.req.formData(),o=r.get("grant_type"),a=r.get("code"),t=r.get("redirect_uri")||void 0;if(o!=="authorization_code"||!a)return n(e,{error:"invalid_request",error_description:"Invalid grant type or missing authorization code"},400);try{const s=await S(a);if(t&&t!==s.redirect_uri)return n(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(process.env.OAUTH_CLIENT_ID&&s.client_id&&s.client_id!==process.env.OAUTH_CLIENT_ID)return n(e,{error:"invalid_client",error_description:"Client mismatch"},400);const c=s.id_token;return typeof c!="string"||c.length===0?n(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400):n(e,{access_token:c,token_type:"Bearer",expires_in:3600,scope:"openid profile email",id_token:c},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch{return n(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(r){const o=r instanceof Error?r.message:String(r);return n(e,{error:"server_error",error_description:"Failed to process token request",error_details:o},500)}}}function b(){return async e=>{const r=new URL(e.req.url);let o=r.searchParams.get("context");if(!o&&r.pathname.startsWith(m(`${d.MCP_CALLBACK}/`))){const t=r.pathname.split("/");o=t[t.length-1]}if(!o)return l.sendMcpAuthorizationFailedMessage({...u(null),error:"Missing context parameter",error_details:null}),e.text("Missing context parameter",400);let a=null;try{const t=await I(o);if(a=t.mcpSessionId||null,!t.isMcpFlow||!t.originalRedirectUri)throw new Error("Invalid MCP context");const s=C(e,"idp_id_token")||C(e,"authorization"),c=await k({idToken:s||"",clientId:t.mcpClientId||"",redirectUri:t.originalRedirectUri,ttlSec:600}),i=new URL(t.originalRedirectUri);return i.searchParams.set("code",c),t.mcpState&&i.searchParams.set("state",t.mcpState),l.sendMcpAuthorizationCompletedMessage({...u(a),redirect_uri:t.originalRedirectUri||null}),e.redirect(i.toString())}catch(t){const s=t instanceof Error?t.message:String(t),c=t instanceof Error?t.stack:String(t);return l.sendMcpAuthorizationFailedMessage({...u(a),error:s,error_details:c}),e.text(`Invalid MCP callback: ${s}`,400)}}}export{T as createMcpContextToken,q as mcpAuthorizationHandler,b as mcpCallbackHandler,$ as mcpDynamicClientRegistrationHandler,D as mcpOAuthAuthorizationServerHandler,O as mcpOAuthProtectedResourceHandler,H as mcpTokenPortalHandler,I as verifyAndParseMcpContextToken};
1
+ import{getCookie as y}from"hono/cookie";import{ulid as S}from"ulid";import{AUTH_URL as _,JWT_SECRET_KEY as C}from"../../constants/common.js";import{ServerRoutes as d}from"../../../constants/common.js";import{withPathPrefix as m}from"@redocly/theme/core/utils";import{telemetry as l}from"../../telemetry/index.js";import{createMcpAuthorizationCode as T,verifyMcpAuthorizationCode as k,createMcpSessionResource as u}from"../auth.js";import*as h from"../jwt/jwt.js";import{AlgorithmTypes as M}from"../jwt/types.js";import{getRequestOrigin as g}from"../utils/get-request-origin.js";const n=(e,r,o=200,a)=>e.json(r,o,{"Content-Type":"application/json",...a??{}});async function I(e){const r=Math.floor(Date.now()/1e3);return h.sign({type:"mcp_context",...e,iat:r,exp:r+600},C,M.HS256)}async function A(e){await h.verify(e,C,M.HS256);const{payload:r}=h.decode(e);if(r.type!=="mcp_context")throw new Error("Invalid context token type");return r}function $(){return async e=>{if(e.req.method!=="GET")return n(e,{error:"Method not allowed"},405,{Allow:"GET"});const r=g(e);return n(e,{resource:`${r}/mcp`,authorization_servers:[r],bearer_methods_supported:["header"],resource_documentation:`${r}/.well-known/oauth-authorization-server`,scopes_supported:["openid","profile","email","offline_access"],bearer_token_types_supported:["Bearer"]})}}function H(){return async e=>{const r=g(e);return n(e,{issuer:_||"",authorization_endpoint:`${r}${d.MCP_AUTHORIZATION}`,token_endpoint:`${r}${d.MCP_TOKEN_PORTAL}`,jwks_uri:`${_||""}/.well-known/jwks.json`,registration_endpoint:`${r}${d.MCP_DYNAMIC_CLIENT_REGISTRATION}`,scopes_supported:["openid","profile","email","offline_access"],response_types_supported:["code"],grant_types_supported:["authorization_code","refresh_token","client_credentials"],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"]})}}function q(){return async e=>{if(e.req.method!=="POST")return n(e,{error:"Method not allowed"},405);try{return n(e,{client_id:process.env.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:["authorization_code","refresh_token"],response_types:["code"],scope:"openid offline email",subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201)}catch(r){return n(e,{error:"invalid_request",error_description:r?.message||"Unable to register client"},500)}}}function b(){return async e=>{const r=new URL(e.req.url),{searchParams:o}=r,a=o.get("redirect_uri"),t=S();l.sendMcpAuthorizationStartedMessage([{...u(t),redirect_uri:a||null}]);const s=g(e),c={isMcpFlow:!0,originalRedirectUri:a,mcpClientId:o.get("client_id"),mcpState:o.get("state"),mcpSessionId:t,timestamp:Date.now()};try{const i=await I(c),p=new URL(d.IDP_LOGIN,s);return p.searchParams.set("redirectTo",`${m(d.MCP_CALLBACK)}/${i}`),p.searchParams.set("idpId","oidc"),e.redirect(p.toString())}catch(i){const p=i instanceof Error?i.message:String(i),w=i instanceof Error?i.stack:String(i);l.sendMcpAuthorizationFailedMessage([{...u(t),error:p,error_details:w}]);const f=new URL(m(`${_}/oauth2/auth`));return f.search=o.toString(),e.redirect(f.toString())}}}function j(){return async e=>{if(e.req.method!=="POST")return n(e,{error:"Method not allowed"},405);try{const r=await e.req.formData(),o=r.get("grant_type"),a=r.get("code"),t=r.get("redirect_uri")||void 0;if(o!=="authorization_code"||!a)return n(e,{error:"invalid_request",error_description:"Invalid grant type or missing authorization code"},400);try{const s=await k(a);if(t&&t!==s.redirect_uri)return n(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(process.env.OAUTH_CLIENT_ID&&s.client_id&&s.client_id!==process.env.OAUTH_CLIENT_ID)return n(e,{error:"invalid_client",error_description:"Client mismatch"},400);const c=s.id_token;return typeof c!="string"||c.length===0?n(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400):n(e,{access_token:c,token_type:"Bearer",expires_in:3600,scope:"openid profile email",id_token:c},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch{return n(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(r){const o=r instanceof Error?r.message:String(r);return n(e,{error:"server_error",error_description:"Failed to process token request",error_details:o},500)}}}function N(){return async e=>{const r=new URL(e.req.url);let o=r.searchParams.get("context");if(!o&&r.pathname.startsWith(m(`${d.MCP_CALLBACK}/`))){const t=r.pathname.split("/");o=t[t.length-1]}if(!o)return l.sendMcpAuthorizationFailedMessage([{...u(null),error:"Missing context parameter",error_details:null}]),e.text("Missing context parameter",400);let a=null;try{const t=await A(o);if(a=t.mcpSessionId||null,!t.isMcpFlow||!t.originalRedirectUri)throw new Error("Invalid MCP context");const s=y(e,"idp_id_token")||y(e,"authorization"),c=await T({idToken:s||"",clientId:t.mcpClientId||"",redirectUri:t.originalRedirectUri,ttlSec:600}),i=new URL(t.originalRedirectUri);return i.searchParams.set("code",c),t.mcpState&&i.searchParams.set("state",t.mcpState),l.sendMcpAuthorizationCompletedMessage([{...u(a),redirect_uri:t.originalRedirectUri||null}]),e.redirect(i.toString())}catch(t){const s=t instanceof Error?t.message:String(t),c=t instanceof Error?t.stack:String(t);return l.sendMcpAuthorizationFailedMessage([{...u(a),error:s,error_details:c}]),e.text(`Invalid MCP callback: ${s}`,400)}}}export{I as createMcpContextToken,b as mcpAuthorizationHandler,N as mcpCallbackHandler,q as mcpDynamicClientRegistrationHandler,H as mcpOAuthAuthorizationServerHandler,$ as mcpOAuthProtectedResourceHandler,j as mcpTokenPortalHandler,A as verifyAndParseMcpContextToken};
@@ -1 +1 @@
1
- import{DEV_LOGIN_SLUG as w,ServerRoutes as N}from"../../../constants/common.js";import{CACHE_CONTROL_NO_CACHE_HEADER_VALUE as a,DEFAULT_TITLE as O}from"../../constants/common.js";import{findInIterable as U}from"../../../utils/collection/find-in-iterable.js";import{removeTrailingSlash as $}from"../../../utils/url/remove-trailing-slash.js";import{isDevelopMode as F}from"../../utils/envs/is-develop-mode.js";import{canAccessResource as q,filterDataByAccessDeep as A,isResourcePubliclyAccessible as B}from"../../utils/rbac.js";import{getServerProps as H}from"../../ssr/index.js";import{readSharedData as V}from"../../utils/index.js";import{getRedirectLoginUrl as G}from"../utils/get-redirect-login-url.js";import{processRedirects as M}from"./helpers/process-redirects.js";import{removeErrorDetails as k}from"../utils/remove-error-details.js";import{telemetry as K}from"../../telemetry/index.js";function ae(e,s){return async(t,m)=>{const l=t.get("logger"),{req:u}=t,{pathname:d}=new URL(u.url),{seo:f,ssoDirect:h}=e.getConfig(),i=f?.title||O;if(e?.compilationErrors?.length&&F())return t.json({templateId:"compilation-error",props:{compilationErrors:e?.compilationErrors},sharedDataIds:{}},500,{"Cache-Control":a});const p=d.match(/page-data(.*)data.json$/);if(!p)return m();const c=decodeURI(p[1]),n=c==="/index/"?"/":$(c),o=e.getRouteBySlug(n,{followRedirect:!1})||U(e.routesBySlug.values(),r=>r.hasClientRoutes&&c.startsWith(r.slug));if(c===N.OIDC_CALLBACK+"/")return t.json({templateId:"403OIDC",sharedDataIds:{},props:{seo:{title:`${i} - Forbidden`}}},200,{"Cache-Control":a});const{isAuthenticated:C,teams:D,claims:{name:S,picture:b,email:I}}=t.get("auth"),R={isAuthenticated:C,email:I,teams:D},g={isAuthenticated:C,name:S,picture:b,email:I,teams:D},E=e.getRedirect(n);if(E){const r=M({redirect:E}).location;return K.sendRedirectMessage({from:n,templateId:"404"}),t.json({templateId:"404",redirectTo:r,sharedDataIds:{},props:{}},301,{"Cache-Control":a})}if(!o){const r=e.getRouteBySlug(n,{followRedirect:!0});return l.error(`Page not found: ${d}`),t.json({templateId:"404",redirectTo:r?.slug,sharedDataIds:{},props:{seo:{title:`${i} - Not Found`}},userData:g},404,{"Cache-Control":a})}if(l.verbose(`Page viewed: ${o.slug}`),!q(o,R,e.config.rbac,e.config.requiresLogin)&&o.slug!==w){if(C)return t.json({templateId:"403",sharedDataIds:{},props:{seo:{title:`${i} - Forbidden`}},userData:g},403,{"Cache-Control":a});const r=Object.keys(h||{}).length>0;return t.json({templateId:"404",sharedDataIds:{},props:{seo:{title:`${i} - Not Found`}},userData:g,...r?{redirectTo:G(e,o.slug)}:{}},r?401:404,{"Cache-Control":a})}const j=A(o.versions,R,e.config.rbac,e.config.requiresLogin),v=e.routesSharedData.get(o.slug)||{},T=await s(o),_=await H(o,t,T,e),{sharedDataIds:y,...L}=_,P={templateId:o.templateId,versions:j,sharedDataIds:{...v,...y||{}},props:process.env.NODE_ENV==="production"?k(L):L,slug:o.slug,userData:g,isPublic:B(o,e.config)};return t.json(P,200,{"Cache-Control":a})}}function se(e){return async(s,t)=>{const m=s.get("logger"),{req:l}=s,{pathname:u}=new URL(l.url),d=u.match(/\/page-data\/shared\/(.*)\.json/);if(!d)return t();const f=decodeURIComponent(d[1]),h=await V(f,e.outdir),{isAuthenticated:i,teams:p,claims:{email:c}}=s.get("auth"),n=A(h,{isAuthenticated:i,email:c,teams:p},e.config.rbac,e.config.requiresLogin);return n?s.json(n,200,{"Cache-Control":a}):(m.error(`Shared data not found: ${u}`),s.text("Not Found",404,{"Cache-Control":a}))}}export{ae as pageDataHandler,se as sharedPageDataHandler};
1
+ import{DEV_LOGIN_SLUG as w,ServerRoutes as N}from"../../../constants/common.js";import{CACHE_CONTROL_NO_CACHE_HEADER_VALUE as a,DEFAULT_TITLE as O}from"../../constants/common.js";import{findInIterable as U}from"../../../utils/collection/find-in-iterable.js";import{removeTrailingSlash as $}from"../../../utils/url/remove-trailing-slash.js";import{isDevelopMode as F}from"../../utils/envs/is-develop-mode.js";import{canAccessResource as q,filterDataByAccessDeep as b,isResourcePubliclyAccessible as B}from"../../utils/rbac.js";import{getServerProps as H}from"../../ssr/index.js";import{readSharedData as V}from"../../utils/index.js";import{getRedirectLoginUrl as G}from"../utils/get-redirect-login-url.js";import{processRedirects as M}from"./helpers/process-redirects.js";import{removeErrorDetails as k}from"../utils/remove-error-details.js";import{telemetry as K}from"../../telemetry/index.js";function ae(e,s){return async(t,m)=>{const l=t.get("logger"),{req:u}=t,{pathname:d}=new URL(u.url),{seo:f,ssoDirect:h}=e.getConfig(),i=f?.title||O;if(e?.compilationErrors?.length&&F())return t.json({templateId:"compilation-error",props:{compilationErrors:e?.compilationErrors},sharedDataIds:{}},500,{"Cache-Control":a});const p=d.match(/page-data(.*)data.json$/);if(!p)return m();const c=decodeURI(p[1]),n=c==="/index/"?"/":$(c),o=e.getRouteBySlug(n,{followRedirect:!1})||U(e.routesBySlug.values(),r=>r.hasClientRoutes&&c.startsWith(r.slug));if(c===N.OIDC_CALLBACK+"/")return t.json({templateId:"403OIDC",sharedDataIds:{},props:{seo:{title:`${i} - Forbidden`}}},200,{"Cache-Control":a});const{isAuthenticated:C,teams:D,claims:{name:j,picture:A,email:I}}=t.get("auth"),R={isAuthenticated:C,email:I,teams:D},g={isAuthenticated:C,name:j,picture:A,email:I,teams:D},E=e.getRedirect(n);if(E){const r=M({redirect:E}).location;return K.sendRedirectMessage([{object:"redirect",from:n,templateId:"404"}]),t.json({templateId:"404",redirectTo:r,sharedDataIds:{},props:{}},301,{"Cache-Control":a})}if(!o){const r=e.getRouteBySlug(n,{followRedirect:!0});return l.error(`Page not found: ${d}`),t.json({templateId:"404",redirectTo:r?.slug,sharedDataIds:{},props:{seo:{title:`${i} - Not Found`}},userData:g},404,{"Cache-Control":a})}if(l.verbose(`Page viewed: ${o.slug}`),!q(o,R,e.config.rbac,e.config.requiresLogin)&&o.slug!==w){if(C)return t.json({templateId:"403",sharedDataIds:{},props:{seo:{title:`${i} - Forbidden`}},userData:g},403,{"Cache-Control":a});const r=Object.keys(h||{}).length>0;return t.json({templateId:"404",sharedDataIds:{},props:{seo:{title:`${i} - Not Found`}},userData:g,...r?{redirectTo:G(e,o.slug)}:{}},r?401:404,{"Cache-Control":a})}const S=b(o.versions,R,e.config.rbac,e.config.requiresLogin),v=e.routesSharedData.get(o.slug)||{},T=await s(o),_=await H(o,t,T,e),{sharedDataIds:y,...L}=_,P={templateId:o.templateId,versions:S,sharedDataIds:{...v,...y||{}},props:process.env.NODE_ENV==="production"?k(L):L,slug:o.slug,userData:g,isPublic:B(o,e.config)};return t.json(P,200,{"Cache-Control":a})}}function se(e){return async(s,t)=>{const m=s.get("logger"),{req:l}=s,{pathname:u}=new URL(l.url),d=u.match(/\/page-data\/shared\/(.*)\.json/);if(!d)return t();const f=decodeURIComponent(d[1]),h=await V(f,e.outdir),{isAuthenticated:i,teams:p,claims:{email:c}}=s.get("auth"),n=b(h,{isAuthenticated:i,email:c,teams:p},e.config.rbac,e.config.requiresLogin);return n?s.json(n,200,{"Cache-Control":a}):(m.error(`Shared data not found: ${u}`),s.text("Not Found",404,{"Cache-Control":a}))}}export{ae as pageDataHandler,se as sharedPageDataHandler};
@@ -0,0 +1,4 @@
1
+ import { WorkerPool } from './worker-pool.js';
2
+ export declare const MCP_TOOL_WORKER_KEY = "executeMcpTool";
3
+ export declare const mcpToolWorkers: WorkerPool;
4
+ //# sourceMappingURL=mcp-tool-worker-pool.d.ts.map
@@ -0,0 +1 @@
1
+ import{getAllowedEnvs as o}from"../utils/envs/get-api-route-allowed-env-variables.js";import{WorkerPool as r}from"./worker-pool.js";const s="executeMcpTool",e=5,c=new r({workerScript:"./mcp-tool-worker",minWorkers:1,maxWorkers:e,workerType:"process",forkOpts:{env:{...o()},stdio:"inherit"},lazy:!0});export{s as MCP_TOOL_WORKER_KEY,c as mcpToolWorkers};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mcp-tool-worker.d.ts.map
@@ -0,0 +1 @@
1
+ import o from"workerpool";import{executeMcpTool as r}from"../plugins/mcp/workers/execute-mcp-tool.js";import{MCP_TOOL_WORKER_KEY as m}from"./mcp-tool-worker-pool.js";o.worker({[m]:r});
@@ -1,9 +1,11 @@
1
1
  import type { ApiRoutesWorkerParams, ApiRoutesWorkerResponse } from '../types/plugins/api-routes.js';
2
2
  import type { RenderPayload } from '../../types/ssr.js';
3
- import type { ScorecardsWorkerParams, ScorecardsWorkerResponse } from '../plugins/scorecards/workers/scorecards.js';
3
+ import type { ScorecardsWorkerParams, ScorecardsWorkerResponse } from '../types/plugins/scorecards.js';
4
+ import type { McpToolWorkerParams, McpToolWorkerResponse } from '../plugins/mcp/types.js';
4
5
  import type { SCORECARDS_WORKER_KEY } from './scorecards-worker-pool.js';
5
6
  import type { API_ROUTES_WORKER_KEY } from './api-routes-worker-pool.js';
6
7
  import type { SSR_WORKER_KEY } from './ssr-worker-pool.js';
8
+ import type { MCP_TOOL_WORKER_KEY } from './mcp-tool-worker-pool.js';
7
9
  export type SsrWorkerResponse = {
8
10
  html: string;
9
11
  statusCode: 200 | 500;
@@ -22,6 +24,10 @@ export type WorkerTypeMapping = {
22
24
  params: [ScorecardsWorkerParams];
23
25
  response: ScorecardsWorkerResponse;
24
26
  };
27
+ [MCP_TOOL_WORKER_KEY]: {
28
+ params: [McpToolWorkerParams];
29
+ response: McpToolWorkerResponse;
30
+ };
25
31
  };
26
32
  export type WorkerParams<T extends keyof WorkerTypeMapping> = WorkerTypeMapping[T]['params'];
27
33
  export type WorkerResponse<T extends keyof WorkerTypeMapping> = WorkerTypeMapping[T]['response'];
@@ -1 +1 @@
1
- const e=()=>process.env.REDOCLY_ENV==="local";export{e as isLocalDevelopment};
1
+ const e=()=>process.env.REDOCLY_ENV==="local"||process.env.REDOCLY_LOCAL_DEV==="true";export{e as isLocalDevelopment};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/realm",
3
- "version": "0.130.0-next.1",
3
+ "version": "0.130.0-next.10",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,16 +23,13 @@
23
23
  "@opentelemetry/core": "2.0.1",
24
24
  "@opentelemetry/exporter-trace-otlp-http": "0.202.0",
25
25
  "@opentelemetry/instrumentation": "0.202.0",
26
- "@opentelemetry/instrumentation-document-load": "0.47.0",
27
- "@opentelemetry/instrumentation-fetch": "0.202.0",
28
26
  "@opentelemetry/instrumentation-http": "0.202.0",
29
- "@opentelemetry/instrumentation-xml-http-request": "0.202.0",
30
27
  "@opentelemetry/resources": "2.0.1",
31
28
  "@opentelemetry/sdk-trace-node": "2.0.1",
32
29
  "@opentelemetry/sdk-trace-web": "2.0.1",
33
30
  "@opentelemetry/semantic-conventions": "1.34.0",
34
- "@redocly/ajv": "8.17.1",
35
- "@redocly/openapi-core": "2.14.3",
31
+ "@redocly/ajv": "8.17.2",
32
+ "@redocly/openapi-core": "2.15.1",
36
33
  "@shikijs/transformers": "3.21.0",
37
34
  "@tanstack/react-query": "5.62.3",
38
35
  "@tanstack/react-table": "8.21.3",
@@ -56,7 +53,7 @@
56
53
  "flexsearch": "0.7.43",
57
54
  "graphql": "16.9.0",
58
55
  "gray-matter": "4.0.3",
59
- "hono": "4.10.6",
56
+ "hono": "4.11.7",
60
57
  "htmlparser2": "8.0.2",
61
58
  "i18next": "22.4.15",
62
59
  "is-glob": "4.0.3",
@@ -71,10 +68,10 @@
71
68
  "os-browserify": "0.3.0",
72
69
  "path-browserify": "1.0.1",
73
70
  "picomatch": "2.3.1",
74
- "react": "19.2.3",
71
+ "react": "19.2.4",
75
72
  "react-calendar": "5.1.0",
76
73
  "react-date-picker": "11.0.0",
77
- "react-dom": "19.2.3",
74
+ "react-dom": "19.2.4",
78
75
  "react-router-dom": "^6.21.1",
79
76
  "react-select": "5.10.1",
80
77
  "reactjs-popup": "2.0.6",
@@ -93,18 +90,18 @@
93
90
  "xml-crypto": "6.0.1",
94
91
  "xpath": "0.0.34",
95
92
  "yaml-ast-parser": "0.0.43",
96
- "@redocly/asyncapi-docs": "1.7.0-next.0",
97
- "@redocly/config": "0.41.2",
98
- "@redocly/graphql-docs": "1.7.0-next.0",
99
- "@redocly/openapi-docs": "3.18.0-next.0",
93
+ "@redocly/asyncapi-docs": "1.7.0-next.9",
94
+ "@redocly/config": "0.41.4",
95
+ "@redocly/graphql-docs": "1.7.0-next.1",
96
+ "@redocly/openapi-docs": "3.18.0-next.9",
100
97
  "@redocly/portal-legacy-ui": "0.13.0-next.0",
101
- "@redocly/portal-plugin-mock-server": "0.15.0-next.0",
102
- "@redocly/realm-asyncapi-sdk": "0.8.0-next.0",
103
- "@redocly/theme": "0.62.0-next.0"
98
+ "@redocly/portal-plugin-mock-server": "0.15.0-next.9",
99
+ "@redocly/realm-asyncapi-sdk": "0.8.0-next.2",
100
+ "@redocly/theme": "0.62.0-next.6"
104
101
  },
105
102
  "peerDependencies": {
106
- "react": "19.2.3",
107
- "react-dom": "19.2.3"
103
+ "react": "19.2.4",
104
+ "react-dom": "19.2.4"
108
105
  },
109
106
  "nx": {
110
107
  "implicitDependencies": [
@@ -1,5 +0,0 @@
1
- import type { Context } from 'hono';
2
- import type { ApiRoute } from '../../../types';
3
- import type { Store } from '../../../store.js';
4
- export declare function runMcpWorker(route: ApiRoute, ctx: Context, store: Store): Promise<Response>;
5
- //# sourceMappingURL=run-api-routes-worker.d.ts.map
@@ -1 +0,0 @@
1
- import*as u from"workerpool";import{withPathPrefix as d}from"@redocly/theme/core/utils";import{TimeoutExceededError as p}from"../../../api-routes/errors/timeout-exceeded.js";import{serializeRequest as l}from"../../../api-routes/helpers/serialize-request.js";import{API_ROUTES_WORKER_KEY as f}from"../../../workers/api-routes-worker-pool.js";import{mcpWorkers as h}from"../../../workers/mcp-worker-pool.js";const w=15;async function A(o,a,t){const e=a.get("auth"),i=await t.resolveRouteStaticData(o)||{},s=d(o.slug);try{const{status:r,headers:m,body:c}=await h.exec(f,[{serverOutDir:t.serverOutDir,slug:s,requestHandlerId:o.requestHandlerId,maxResponseSizeMB:w,ctxData:{user:{teams:e.teams,email:e.claims.email,claims:e.claims,idpAccessToken:e.idpAccessToken,idpId:e.claims.idpId,isAuthenticated:e.isAuthenticated},config:t.config},staticData:{...i,props:{...i.props,routeSlug:s,outdir:t.outdir}},req:await l(a.req.raw)}]),n=c.data?Buffer.from(c.data):null;return new Response(n,{status:r,headers:m})}catch(r){throw r instanceof u.Promise.TimeoutError?new p("Timeout exceeded"):r}}export{A as runMcpWorker};
@@ -1,4 +0,0 @@
1
- import { WorkerPool } from './worker-pool.js';
2
- export declare const API_ROUTES_WORKER_KEY = "executeApiRoute";
3
- export declare const mcpWorkers: WorkerPool;
4
- //# sourceMappingURL=mcp-worker-pool.d.ts.map
@@ -1 +0,0 @@
1
- import{getAllowedEnvs as r}from"../utils/envs/get-api-route-allowed-env-variables.js";import{WorkerPool as o}from"./worker-pool.js";const p="executeApiRoute",e=5,i=new o({workerScript:"./api-routes-worker",minWorkers:1,maxWorkers:e,workerType:"process",forkOpts:{env:{...r()},stdio:"inherit"},lazy:!0});export{p as API_ROUTES_WORKER_KEY,i as mcpWorkers};