@redocly/realm 0.130.0-next.10 → 0.130.0-next.11
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.
- package/CHANGELOG.md +22 -0
- package/dist/server/plugins/catalog-entities/database/catalog-entities-service.d.ts +32 -9
- package/dist/server/plugins/catalog-entities/database/catalog-entities-service.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-bff-repository.d.ts +16 -5
- package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-bff-repository.js +21 -17
- package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-read-repository.d.ts +35 -9
- package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-read-repository.js +24 -21
- package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-repository.d.ts +31 -8
- package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-repository.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-write-repository.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-relations-repository.d.ts +7 -1
- package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-relations-repository.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/remote/catalog-entities-remote-repository.d.ts +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/remote/catalog-entities-remote-repository.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/utils/build-entities-exclusion-filter.d.ts +13 -0
- package/dist/server/plugins/catalog-entities/database/repositories/utils/build-entities-exclusion-filter.js +1 -0
- package/dist/server/plugins/catalog-entities/database/repositories/utils/build-rbac-filter.d.ts +31 -0
- package/dist/server/plugins/catalog-entities/database/repositories/utils/build-rbac-filter.js +9 -0
- package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/base.js +1 -1
- package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/openapi-entities-extractor.js +1 -1
- package/dist/server/plugins/catalog-entities/extensions/extractors/fs-entities-extractor.js +1 -1
- package/dist/server/plugins/catalog-entities/get-server-props.js +1 -1
- package/dist/server/plugins/catalog-entities/types/openapi.d.ts +11 -0
- package/dist/server/plugins/catalog-entities/types/openapi.js +0 -0
- package/dist/server/plugins/catalog-entities/types/params.d.ts +6 -0
- package/dist/server/plugins/catalog-entities/types/params.js +0 -0
- package/dist/server/plugins/catalog-entities/utils/catalog-data-collector.js +1 -1
- package/dist/server/plugins/catalog-entities/utils/get-not-accessible-catalog-resources.d.ts +11 -0
- package/dist/server/plugins/catalog-entities/utils/get-not-accessible-catalog-resources.js +1 -0
- package/dist/server/plugins/search/llmstxt/index.js +4 -4
- package/dist/server/utils/rbac.d.ts +65 -0
- package/dist/server/web-server/auth.js +3 -3
- package/dist/server/web-server/middleware/catalogAuthMiddleware.d.ts +4 -6
- package/dist/server/web-server/middleware/catalogAuthMiddleware.js +1 -1
- package/dist/server/web-server/routes/catalog/bff-catalog-related-entities.js +1 -1
- package/dist/server/web-server/routes/catalog/bff-catalog.js +1 -1
- package/dist/server/web-server/routes/catalog/catalog.js +1 -1
- package/dist/server/web-server/routes/catalog/helpers/has-access-to-entity.d.ts +10 -0
- package/dist/server/web-server/routes/catalog/helpers/has-access-to-entity.js +1 -0
- package/dist/server/web-server/routes/helpers/get-current-rbac-teams.d.ts +3 -0
- package/dist/server/web-server/routes/helpers/get-current-rbac-teams.js +1 -0
- package/dist/server/web-server/routes/helpers/get-rbac-restrictions-data-for-catalog.d.ts +11 -0
- package/dist/server/web-server/routes/helpers/get-rbac-restrictions-data-for-catalog.js +1 -0
- package/dist/server/web-server/routes/index.js +1 -1
- package/package.json +9 -9
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import"../node-crypto-polyfill.js";import{DOMParser as
|
|
1
|
+
import"../node-crypto-polyfill.js";import{DOMParser as D}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 N,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 k}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 R,encodeBase64URL as oe,urlSafeBase64 as v}from"./jwt/encode.js";import{formatSamlCertificate as se}from"./utils/format-saml-certificate.js";function j(e){return e?.type===u.OIDC}function ie(e){return e?.type===u.SAML2}async function ze(e,t){if(j(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(N)),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(N));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:k(),idpId:s,redirectUri:f,redirectTo:m,branch:S?.branchOverride??me(e),inviteCode:A,source:S?.sourceOverride??"portal"},h={};if(l){const d=v(te(50)),_=v(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
|
-
ID="_${
|
|
4
|
+
ID="_${k()}"
|
|
5
5
|
IssueInstant="${new Date().toISOString()}"
|
|
6
6
|
AssertionConsumerServiceURL="${e}${P.SAML_CALLBACK}"
|
|
7
7
|
AttributeConsumingServiceIndex="0">
|
|
@@ -9,4 +9,4 @@ import"../node-crypto-polyfill.js";import{DOMParser as b}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:r,inviteCode:n,source:"portal"})})}}function he(e){return ae(H(new TextEncoder().encode(e)).buffer)}function Ke(e){const t=
|
|
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=R(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(R(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function Xe(e){const t=new D().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(_),T={},C=i(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(C.length)for(const O of C){const U=i(O,"./@Name")[0];if(U.nodeValue){const b=i(O,"./*[local-name(.)='AttributeValue']/text()")[0];b?.nodeValue&&(T[U.nodeValue]=b.nodeValue)}}return{uid:x,success:n,expiresAt:g,issuerId:l,entityId:A,attrs:T,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(!j(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 D().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.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||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,j 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,8 +1,6 @@
|
|
|
1
1
|
import type { Context, Next } from 'hono';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
export declare function catalogAuthMiddleware(serverOutDir: string, options?: CatalogAuthMiddlewareOptions): (ctx: Context, next: Next) => Promise<Response | void>;
|
|
7
|
-
export {};
|
|
2
|
+
export declare function catalogAuthMiddleware({ serverOutDir, protectReadMethods, }: {
|
|
3
|
+
serverOutDir: string;
|
|
4
|
+
protectReadMethods?: boolean;
|
|
5
|
+
}): (ctx: Context, next: Next) => Promise<Response | void>;
|
|
8
6
|
//# sourceMappingURL=catalogAuthMiddleware.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{KvService as
|
|
1
|
+
import{KvService as p}from"../../persistence/kv/services/kv-service.js";import{DEFAULT_AUTHENTICATED_TEAM as A}from"../../../constants/common.js";import{JWT_SECRET_KEY as l}from"../../constants/common.js";import*as d from"../jwt/jwt.js";import{AlgorithmTypes as y}from"../jwt/types.js";const I=60,T=e=>["POST","PUT","DELETE","PATCH"].includes(e),h=e=>["GET"].includes(e);function D({serverOutDir:e,protectReadMethods:t=!0}){return async(r,a)=>await f(r,a,e,t)}const f=async(e,t,r,a=!0)=>{const s=e.req.method,n=T(s)||h(s)&&a,o=e.req.header("apiKey");if(o)return await v(e,t,o,r);const c=e.req.header("authorization")?.replace("Bearer ","");return c?await w(e,t,c):n?e.json({message:"API key is required"},401):await t()},v=async(e,t,r,a)=>{if(!process.env.BH_API_URL||!process.env.ORGANIZATION_ID)return e.json({message:"API key validation service not configured"},500);try{const s=await p.getInstance({baseDbDir:a});let n=await E(s,r);if(n)return e.set("apiKeyTeams",n.teams),await t();const o=new URL(`/api/orgs/${process.env.ORGANIZATION_ID}/session`,process.env.BH_API_URL).toString(),i=await fetch(o,{method:"GET",headers:{Authorization:`Bearer ${r}`}});if(!i.ok)return e.json({message:"Invalid API key"},401);const m=(await i.json())?.user?.teams?.[process.env.ORGANIZATION_ID]??[],u=[A,...m];return e.set("apiKeyTeams",u),await s.set(["api-keys","reunite",r],{valid:!0,teams:u},{ttlInSeconds:I}),await t()}catch{return e.json({message:"API key validation failed"},400)}},w=async(e,t,r)=>{try{const a=await d.verify(r,l,y.HS256),s=d.decode(r).payload.isInternalConnection;return!a||!s?e.json({message:"API key is required"},401):await t()}catch{return e.json({message:"API key validation failed"},400)}},E=async(e,t)=>{try{return await e.get(["api-keys","reunite",t])}catch{return null}};export{D as catalogAuthMiddleware};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{telemetryTraceStep as
|
|
1
|
+
import{telemetryTraceStep as o}from"../../../telemetry/helpers/trace-step.js";import{CATALOG_ENTITY_KEY as d}from"../../../../constants/common.js";import{ALLOWED_CATALOG_QUERY_PARAMS as y}from"../../../constants/plugins/catalog-entities.js";import{allowlistObject as g}from"../../../../utils/object/allowlist-object.js";import{CatalogEntitiesService as f}from"../../../plugins/catalog-entities/database/catalog-entities-service.js";import{createPaginationParamsValidator as E}from"../../../providers/database/pagination/schemas.js";import{ENTITY_RELATION_FROM_DATABASE as A}from"../../../plugins/catalog-entities/database/mappers/field-transformations.js";import{getRbacRestrictionsDataForCatalog as b}from"../helpers/get-rbac-restrictions-data-for-catalog.js";const _=["id","key","title","type","summary","source","sourceFile","createdAt","updatedAt","version","revision"],p=async({catalogEntitiesService:i,store:a,ctx:e})=>o("catalog_entities.bff.related_entities.get_related_entities",async t=>{try{const r=e.req.param(d);if(!r)return t?.error(new Error("Entity key is required")),e.json({message:"Entity key is required"},400);t?.setAttribute("pathParams",JSON.stringify({entityKey:r}));const s=e.req.query();t?.setAttribute("queryParams",JSON.stringify(g(s,y)));const m=E(_,A).parse(s),{currentRbacTeamsForRead:l,excludedTypes:c,excludedEntities:u}=b({store:a,ctx:e}),n=await i.getRelatedEntities({entityKey:r,paginationParams:m,rbacTeams:l,excludedTypes:c,excludedEntities:u});return t?.setAttribute("relatedEntitiesCount",n.items.length),e.json(n)}catch(r){return console.error(r),t?.error(r),e.json({message:r.message},500)}});function v(i){return async a=>o("catalog_entities.bff.related_entities",async e=>{e?.setAttribute("method",a.req.method);const t=await f.getInstance({baseDbDir:i.serverOutDir});return p({catalogEntitiesService:t,ctx:a,store:i})})}export{v as bffCatalogRelatedEntitiesHandler};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{telemetryTraceStep as d}from"../../../telemetry/helpers/trace-step.js";import{CATALOG_ENTITY_KEY as g}from"../../../../constants/common.js";import{ALLOWED_CATALOG_QUERY_PARAMS as p}from"../../../constants/plugins/catalog-entities.js";import{allowlistObject as E}from"../../../../utils/object/allowlist-object.js";import{CatalogEntitiesService as b}from"../../../plugins/catalog-entities/database/catalog-entities-service.js";import{createPaginationParamsValidator as v}from"../../../providers/database/pagination/schemas.js";import{isValidIsoDate as A}from"../../../utils/is-valid-iso-date.js";import{isValidSanitizedString as q}from"../../../utils/validate-and-sanitize-string.js";import{getRbacRestrictionsDataForCatalog as l}from"../helpers/get-rbac-restrictions-data-for-catalog.js";const h=["type","key","title","summary","tags","metadata","metadata.*","git","contact","links","id","source","sourceFile","version","revision","createdAt","updatedAt","domains","owners"],w=async({catalogEntitiesService:r,ctx:e,store:n})=>d("catalog_entities.bff.get_entities",async t=>{const i=e.req.query();t?.setAttribute("queryParams",JSON.stringify(E(i,p)));const a=v(h).parse(i),{currentRbacTeamsForRead:s,excludedTypes:o,excludedEntities:u}=l({store:n,ctx:e}),m=await r.getEntitiesWithRelations({paginationParams:a,rbacTeams:s,excludedTypes:o,excludedEntities:u});return t?.setAttribute("entitiesCount",m.items.length),e.json(m)}),R=async({catalogEntitiesService:r,ctx:e,store:n})=>d("catalog_entities.bff.get_entity",async t=>{const i=e.req.param(g);if(!i)return t?.error(new Error("Entity key is required")),e.json({message:"Entity key is required"},400);const a=e.req.query("revision");let s=null;if(a){if(!A(a))return t?.error(new Error("Invalid revision parameter")),e.json({message:"Invalid revision parameter: must be a valid ISO 8601 date-time string"},400);s=a}const o=e.req.query("version");if(!q(o,{pattern:/^[a-zA-Z0-9._-]+$/,maxLength:100,allowEmpty:!0}))return t?.error(new Error("Invalid version parameter")),e.json({message:"Invalid version parameter: version must contain only alphanumeric characters, dots, hyphens, and underscores, and must not exceed 100 characters"},400);const u=e.req.query();t?.setAttribute("queryParams",JSON.stringify(u)),t?.setAttribute("pathParams",JSON.stringify({entityKey:i}));const{currentRbacTeamsForRead:m,excludedTypes:c,excludedEntities:f}=l({store:n,ctx:e}),y=await r.getEntityWithRelationsByKey({entityKey:i,filter:{revision:s,version:o},rbacTeams:m,excludedTypes:c,excludedEntities:f});return y?(t?.setAttribute("entity",JSON.stringify(y)),e.json(y)):(t?.error(new Error("Entity not found")),e.json({message:"Entity not found"},404))});function F(r){return async e=>d("catalog_entities.bff",async n=>{n?.setAttribute("method",e.req.method);const t=await b.getInstance({baseDbDir:r.serverOutDir});return e.req.param("entityKey")?R({catalogEntitiesService:t,ctx:e,store:r}):w({catalogEntitiesService:t,ctx:e,store:r})})}export{F as bffCatalogHandler};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getCompleteCatalogConfig as
|
|
1
|
+
import{getCompleteCatalogConfig as w}from"../../../plugins/catalog-entities/get-complete-catalog-config.js";import{parseAndValidateEntities as h,parseAndValidateEntity as p,parseAndValidateEntityUpdate as b}from"../../../plugins/catalog-entities/entities/validate-entity.js";import{telemetryTraceStep as u}from"../../../telemetry/helpers/trace-step.js";import{CATALOG_FILTERS_CACHE_NAMESPACE as T,ALLOWED_CATALOG_QUERY_PARAMS as j}from"../../../constants/plugins/catalog-entities.js";import{CATALOG_ENTITY_ID as m}from"../../../../constants/common.js";import{allowlistObject as _}from"../../../../utils/object/allowlist-object.js";import{isValidIsoDate as q}from"../../../utils/is-valid-iso-date.js";import{CatalogEntitiesService as O}from"../../../plugins/catalog-entities/database/catalog-entities-service.js";import{createPaginationParamsValidator as v}from"../../../providers/database/pagination/schemas.js";import{createEntityRelationDtoFromFileSchema as C}from"../../../plugins/catalog-entities/database/mappers/create-entity-relation-dto-from-file-schema.js";import{CacheService as F}from"../../../persistence/cache/services/cache-service.js";import{hasAccessToEntity as E}from"./helpers/has-access-to-entity.js";import{getRbacRestrictionsDataForCatalog as A}from"../helpers/get-rbac-restrictions-data-for-catalog.js";const R=["type","key","title","summary","tags","metadata","metadata.*","git","contact","links","id","source","sourceFile","createdAt","updatedAt"],f=async n=>{await(await F.getInstance({baseDbDir:n})).deleteByNamespace(T)},I=async({catalogEntitiesService:n,ctx:e,store:y})=>u("catalog_entities.get_entities",async i=>{const a=e.get("logger");try{const t=e.req.query();i?.setAttribute("queryParams",JSON.stringify(_(t,j)));const{currentRbacTeamsForRead:o,excludedTypes:r,excludedEntities:s}=A({store:y,ctx:e}),d=v(R).parse(t),c=await n.getEntities({paginationParams:d,rbacTeams:o,excludedTypes:r,excludedEntities:s});return i?.setAttribute("entitiesCount",c.items.length),e.json(c)}catch(t){return a.error(t),i?.error(t),e.json({message:"Failed to get entities"},500)}}),D=async({catalogEntitiesService:n,ctx:e,store:y})=>u("catalog_entities.get_entity",async i=>{const a=e.req.param(m);if(!a)return i?.error(new Error("Entity id is required")),e.json({message:"Entity id is required"},400);i?.setAttribute("pathParams",JSON.stringify({entityId:a}));const{currentRbacTeamsForRead:t,excludedTypes:o,excludedEntities:r}=A({store:y,ctx:e}),s=await n.getEntityById(a,{rbacTeams:t,excludedTypes:o,excludedEntities:r});return s?(i?.setAttribute("entity",JSON.stringify(s)),e.json(s)):(i?.error(new Error("Entity not found")),e.json({message:"Entity not found"},404))}),P=async({catalogEntitiesService:n,ctx:e,catalogConfig:y,serverOutDir:i,store:a})=>u("catalog_entities.create_entity",async t=>{const o=e.get("logger");try{const r=await e.req.json();t?.setAttribute("requestBody",JSON.stringify(r));const s=p(r,y);if(!E({ctx:e,store:a,accessLevel:"WRITE",entityType:s.type,entityKey:s.key}))return t?.error(new Error("Access denied")),e.json({message:"Access denied"},403);const c=await n.createEntity(s),l=s.relations?.map(g=>C(s.key,g));return await n.createEntitiesRelations(l??[]),c?(f(i),t?.setAttribute("entity",JSON.stringify(c)),e.json(c)):(t?.error(new Error("Failed to create entity")),e.json({message:"Failed to create entity"},500))}catch(r){return o.error(r),r instanceof Error&&r.message.includes("validation failed")?(t?.error(new Error(r.message)),e.json({message:r.message},400)):(t?.error(r),e.json({message:"Failed to create entity"},500))}}),N=async({catalogEntitiesService:n,ctx:e,catalogConfig:y,serverOutDir:i,store:a})=>u("catalog_entities.bulk_upsert_entities",async t=>{const o=e.get("logger");try{const r=await e.req.json();t?.setAttribute("requestBody",JSON.stringify(r));const s=h(r,y);for(const l of s)if(!E({ctx:e,store:a,accessLevel:"WRITE",entityType:l.type,entityKey:l.key}))return t?.error(new Error("Access denied")),e.json({message:"Access denied"},403);const d=await n.createEntities(s);if(!d.length)return t?.error(new Error("Failed to create entities")),e.json({message:"Failed to create entities"},500);f(i);const c=d.filter(l=>l.status==="ok");return t?.setAttribute("totalSuccess",c.length),t?.setAttribute("totalFailed",d.length-c.length),e.json(d,207)}catch(r){return o.error(r),r instanceof Error&&r.message.includes("validation failed")?(t?.error(new Error(r.message)),e.json({message:r.message},400)):(t?.error(r),e.json({message:"Failed to create entities"},500))}}),W=async({catalogEntitiesService:n,ctx:e,catalogConfig:y,serverOutDir:i,store:a})=>u("catalog_entities.update_entity",async t=>{const o=e.get("logger"),r=e.req.param(m);if(!r)return t?.error(new Error("Entity id is required")),e.json({message:"Entity id is required"},400);t?.setAttribute("pathParams",JSON.stringify({entityId:r}));try{const s=await e.req.json();t?.setAttribute("requestBody",JSON.stringify(s));const d=await n.getEntityById(r);if(!d)return t?.error(new Error(`Entity with id: ${r} not found`)),e.json({message:`Entity with id: ${r} not found`},404);if(!E({ctx:e,store:a,accessLevel:"WRITE",entityType:s.type??d.type,entityKey:s.key??d.key}))return t?.error(new Error("Access denied")),e.json({message:"Access denied"},403);const l=b(s,y,s?.type??d.type);if(l.revision&&!q(l.revision))throw new Error("Entity validation failed: 'entity.revision' must be a valid ISO 8601 date-time string");const g=await n.updateEntity(l,d);return g?(f(i),t?.setAttribute("entity",JSON.stringify(g)),e.json(g)):(t?.error(new Error("Failed to update entity")),e.json({message:"Failed to update entity"},500))}catch(s){return o.error(s),s instanceof Error&&s.message.includes("validation failed")?(t?.error(new Error(s.message)),e.json({message:s.message},400)):(t?.error(s),e.json({message:s.message},500))}}),k=async({catalogEntitiesService:n,ctx:e,serverOutDir:y,store:i})=>u("catalog_entities.delete_entity",async a=>{const t=e.req.param(m);if(!t)return a?.error(new Error("Entity id is required")),e.json({message:"Entity id is required"},400);a?.setAttribute("pathParams",JSON.stringify({entityId:t}));const o=await n.getEntityById(t);return o?E({ctx:e,store:i,accessLevel:"WRITE",entityType:o.type,entityKey:o.key})?(await n.deleteEntity(o),f(y),new Response(null,{status:204})):(a?.error(new Error("Access denied")),e.json({message:"Access denied"},403)):new Response(null,{status:204})}),L={GET:I,POST:P,PUT:N},J={GET:D,DELETE:k,PATCH:W};function S(n){return async e=>u("catalog_entities",async y=>{const i=await O.getInstance({baseDbDir:n.serverOutDir}),a=e.req.method;y?.setAttribute("method",a);const t=e.req.param(m)?J:L,o=w(n.config.entitiesCatalog),r=t[a];return r?await r({catalogEntitiesService:i,ctx:e,catalogConfig:o,serverOutDir:n.serverOutDir,store:n}):(y?.error(new Error("Method not allowed")),e.json({message:"Method not allowed"},405))})}export{S as catalogHandler};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
import type { Store } from '../../../../store';
|
|
3
|
+
export declare function hasAccessToEntity({ ctx, store, accessLevel, entityType, entityKey, }: {
|
|
4
|
+
ctx: Context;
|
|
5
|
+
store: Store;
|
|
6
|
+
accessLevel: 'READ' | 'WRITE';
|
|
7
|
+
entityType: string;
|
|
8
|
+
entityKey: string;
|
|
9
|
+
}): boolean;
|
|
10
|
+
//# sourceMappingURL=has-access-to-entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getNotAccessibleCatalogResources as a}from"../../../../plugins/catalog-entities/utils/get-not-accessible-catalog-resources.js";import{getCurrentRbacTeams as f}from"../../helpers/get-current-rbac-teams.js";function g({ctx:e,store:t,accessLevel:s,entityType:c,entityKey:r}){const n=t.getConfig().rbac||{},o=f(e),{types:i,entities:u}=a({rbacConfig:n,currentRbacTeams:o,accessLevel:s});return!(i.includes(c)||u.includes(r))}export{g as hasAccessToEntity};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function t(e){return e.get("apiKeyTeams")?.length?e.get("apiKeyTeams"):e.get("auth")?.teams||[]}export{t as getCurrentRbacTeams};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
import type { Store } from '../../../store.js';
|
|
3
|
+
export declare function getRbacRestrictionsDataForCatalog({ store, ctx }: {
|
|
4
|
+
store: Store;
|
|
5
|
+
ctx: Context;
|
|
6
|
+
}): {
|
|
7
|
+
currentRbacTeamsForRead: string[];
|
|
8
|
+
excludedTypes: string[];
|
|
9
|
+
excludedEntities: string[];
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=get-rbac-restrictions-data-for-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getNotAccessibleCatalogResources as n}from"../../../plugins/catalog-entities/utils/get-not-accessible-catalog-resources.js";import{getCurrentRbacTeams as i}from"./get-current-rbac-teams.js";import{expandTeamsForRead as m}from"../../../utils/rbac.js";function b({store:o,ctx:r}){const e=o.getConfig().rbac||{},t=i(r),a=m(e,t),{types:c,entities:s}=n({rbacConfig:e,currentRbacTeams:t});return{currentRbacTeamsForRead:a,excludedTypes:c,excludedEntities:s}}export{b as getRbacRestrictionsDataForCatalog};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{serveStatic as
|
|
1
|
+
import{serveStatic as H}from"hono/serve-static";import{withPathPrefix as e,withoutPathPrefix as R}from"@redocly/theme/core/utils";import{ServerRoutes as l}from"../../../constants/common.js";import{PUBLIC_STATIC_FOLDER as L}from"../../constants/common.js";import{authMiddleware as C}from"../middleware/authMiddleware.js";import{ensureSearchData as c}from"../middleware/ensureSearchData.js";import{dynamicMiddleware as g}from"../middleware/dynamic-middleware/dynamic-middleware.js";import{installRoutes as S}from"../../plugins/dev-onboarding/api/routes/index.js";import{authorizeHandler as I,oidcCallbackHandler as P,logoutHandler as A,postLogoutHandler as D,idpLoginHandler as h,redoclyLoginCallbackHandler as M,samlCallbackHandler as N,redoclyTokenLoginHandler as v,inviteHandler as B}from"./auth.js";import{appDataHandler as U}from"./app-data.js";import{searchFacetsHandler as w,searchHandler as G}from"./search.js";import{dynamicRouteHandler as F}from"./dynamic-route.js";import{pageDataHandler as K,sharedPageDataHandler as y}from"./page-data.js";import{pathPrefixRedirectHandler as k}from"./path-prefix-redirect.js";import{getRoutesByLineHandler as _,resolvePathHandler as O,resolvePathsHandler as Y,resolveSlugHandler as E}from"./resolve-route.js";import{feedbackHandler as b}from"./feedback.js";import{loggerMiddleware as V}from"../middleware/loggerMiddleware.js";import{responseHeadersMiddleware as x}from"../middleware/responseHeadersMiddleware.js";import{idleTimeoutMiddleware as z}from"../middleware/idleTimeoutMiddleware.js";import{otelTracesHandler as Z}from"./otel/otel.js";import{healthCheckHandler as u}from"./health.js";import{askAiHandler as $}from"./ask-ai.js";import{replayOauth2RedirectCallbackHandler as q}from"./replay-oauth2-redirect.js";import{mcpOAuthProtectedResourceHandler as W,mcpOAuthAuthorizationServerHandler as j,mcpDynamicClientRegistrationHandler as J,mcpAuthorizationHandler as Q,mcpTokenPortalHandler as X,mcpCallbackHandler as r}from"./mcp-oauth.js";import{corsMiddleware as T}from"../middleware/corsMiddleware.js";import{installApiRoutes as aa}from"./api-routes/api-routes.js";import{cookieMiddleware as ea}from"../middleware/cookieMiddleware.js";import{staticContentHandler as ia}from"../routes/static-content.js";import{infoHandler as s}from"./info.js";import{catalogHandler as la}from"./catalog/catalog.js";import{catalogRelationsHandler as ta}from"./catalog/catalog-relations.js";import{bffCatalogHandler as ma}from"./catalog/bff-catalog.js";import{bffCatalogRevisionsHandler as da}from"./catalog/bff-catalog-revisions.js";import{bffCatalogRelatedEntitiesHandler as na}from"./catalog/bff-catalog-related-entities.js";import{catalogAuthMiddleware as t}from"../middleware/catalogAuthMiddleware.js";import{telemetryMiddleware as oa}from"../middleware/telemetry-middleware.js";import{errorHandler as pa}from"./error.js";function qa(a,i,m){const{resolveRouteData:n,readStaticAsset:o}=m;a.use("*",z()),a.use("*",ea()),a.use("*",g(i)),a.use("*",C(i)),a.use("*",V()),a.use("*",x(i)),a.use("*",oa()),a.use(e("*"),H({root:`./${L}`,getContent:(d,f)=>ia(d,f,i,o),rewriteRequestPath:d=>R(d)})),a.use(e(l.FEEDBACK),T({allowMethods:["POST"]})),a.use(e(l.ASK_AI),T({allowMethods:["POST"]})),a.use("*",Aa(i));const p=c(i);a.use(e(l.INFO),s()),process.env.NEW_CATALOG_ENABLED==="true"&&(a.use(e(l.CATALOG_ENTITIES),t({serverOutDir:i.serverOutDir}),la(i)),a.use(e(l.CATALOG_ENTITIES_RELATIONS),t({serverOutDir:i.serverOutDir}),ta(i)),a.get(e(l.BFF_CATALOG_ENTITIES),t({serverOutDir:i.serverOutDir,protectReadMethods:!1}),ma(i)),a.get(e(l.BFF_CATALOG_RELATED_ENTITIES),t({serverOutDir:i.serverOutDir,protectReadMethods:!1}),na(i)),a.get(e(l.BFF_CATALOG_REVISIONS),t({serverOutDir:i.serverOutDir,protectReadMethods:!1}),da(i))),a.get(e(l.SHARED_PAGE_DATA),y(i)),a.get(e(l.PAGE_DATA),K(i,n)),a.get(e(l.APP_DATA),U(i)),a.post(e(l.SEARCH),p,G(i)),a.post(e(l.SEARCH_FACETS),p,w(i)),a.post(e(l.AUTHORIZATION),I),a.post(e(l.LOGOUT),A(i)),a.get(e(l.LOGOUT),A(i)),a.get(e(l.POST_LOGOUT),D(i)),a.get(e(l.OIDC_CALLBACK),P(i)),a.get(e(l.REDOCLY_TOKEN_LOGIN),v(i)),a.get(e(l.REDOCLY_LOGIN_CALLBACK),M()),a.get(e(l.IDP_LOGIN),h(i)),a.post(e(l.SAML_CALLBACK),N(i)),a.get(e(l.INVITE),B(i)),a.get(e(l.HEALTH),u),a.get(e(l.MCP_OAUTH_PROTECTED_RESOURCE),W()),a.get(e(l.MCP_OAUTH_AUTHORIZATION_SERVER),j()),a.post(e(l.MCP_DYNAMIC_CLIENT_REGISTRATION),J()),a.get(e(l.MCP_AUTHORIZATION),Q()),a.post(e(l.MCP_TOKEN_PORTAL),X()),a.get(e(l.MCP_CALLBACK),r()),a.get(e(`${l.MCP_CALLBACK}/*`),r()),S(a,i),aa(a,i),a.post(e(l.FEEDBACK),b(i)),a.post(e(l.RESOLVE_ROUTE_BY_PATH),O(i)),a.post(e(l.RESOLVE_ROUTES_BY_PATHS),Y(i)),a.post(e(l.RESOLVE_ROUTE_BY_SLUG),E(i)),a.post(e(l.ASK_AI),$(i)),a.get(e(l.GET_ROUTES_BY_LINE),_(i)),a.post(e(l.OTEL_TRACES),Z),a.get(e(l.REPLAY_OAUTH2_CALLBACK),q),a.all(e("/*"),F(i,n,o)),a.get("*",k),a.onError(pa)}function Aa(a){return async(i,m)=>{await a.waitForPluginsLifecycle(),await m()}}function Wa(a,i){a.get(e(l.INFO),s()),a.post(e(l.RESOLVE_ROUTE_BY_PATH),O(i)),a.post(e(l.RESOLVE_ROUTE_BY_SLUG),E(i)),a.get(e(l.GET_ROUTES_BY_LINE),_(i))}export{Wa as installDevRoutes,qa as installProdRoutes,Aa as waitForPluginsLifecycle};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/realm",
|
|
3
|
-
"version": "0.130.0-next.
|
|
3
|
+
"version": "0.130.0-next.11",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@opentelemetry/sdk-trace-web": "2.0.1",
|
|
30
30
|
"@opentelemetry/semantic-conventions": "1.34.0",
|
|
31
31
|
"@redocly/ajv": "8.17.2",
|
|
32
|
-
"@redocly/openapi-core": "2.15.
|
|
32
|
+
"@redocly/openapi-core": "2.15.2",
|
|
33
33
|
"@shikijs/transformers": "3.21.0",
|
|
34
34
|
"@tanstack/react-query": "5.62.3",
|
|
35
35
|
"@tanstack/react-table": "8.21.3",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"react-calendar": "5.1.0",
|
|
73
73
|
"react-date-picker": "11.0.0",
|
|
74
74
|
"react-dom": "19.2.4",
|
|
75
|
-
"react-router-dom": "^6.
|
|
75
|
+
"react-router-dom": "^6.30.3",
|
|
76
76
|
"react-select": "5.10.1",
|
|
77
77
|
"reactjs-popup": "2.0.6",
|
|
78
78
|
"semver": "7.7.3",
|
|
@@ -90,14 +90,14 @@
|
|
|
90
90
|
"xml-crypto": "6.0.1",
|
|
91
91
|
"xpath": "0.0.34",
|
|
92
92
|
"yaml-ast-parser": "0.0.43",
|
|
93
|
-
"@redocly/asyncapi-docs": "1.7.0-next.
|
|
93
|
+
"@redocly/asyncapi-docs": "1.7.0-next.10",
|
|
94
94
|
"@redocly/config": "0.41.4",
|
|
95
|
-
"@redocly/graphql-docs": "1.7.0-next.
|
|
96
|
-
"@redocly/openapi-docs": "3.18.0-next.
|
|
95
|
+
"@redocly/graphql-docs": "1.7.0-next.2",
|
|
96
|
+
"@redocly/openapi-docs": "3.18.0-next.10",
|
|
97
97
|
"@redocly/portal-legacy-ui": "0.13.0-next.0",
|
|
98
|
-
"@redocly/portal-plugin-mock-server": "0.15.0-next.
|
|
99
|
-
"@redocly/realm-asyncapi-sdk": "0.8.0-next.
|
|
100
|
-
"@redocly/theme": "0.62.0-next.
|
|
98
|
+
"@redocly/portal-plugin-mock-server": "0.15.0-next.10",
|
|
99
|
+
"@redocly/realm-asyncapi-sdk": "0.8.0-next.3",
|
|
100
|
+
"@redocly/theme": "0.62.0-next.7"
|
|
101
101
|
},
|
|
102
102
|
"peerDependencies": {
|
|
103
103
|
"react": "19.2.4",
|