@redocly/realm 0.138.0-next.12 → 0.138.0-next.13
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 +29 -0
- package/dist/client/app/pages/Invite/Invite.js +1 -1
- package/dist/constants/l10n/langs/ar.js +1 -1
- package/dist/constants/l10n/langs/de.js +1 -1
- package/dist/constants/l10n/langs/en.js +1 -1
- package/dist/constants/l10n/langs/es.js +1 -1
- package/dist/constants/l10n/langs/fr.js +1 -1
- package/dist/constants/l10n/langs/hi.js +1 -1
- package/dist/constants/l10n/langs/it.js +1 -1
- package/dist/constants/l10n/langs/ja.js +1 -1
- package/dist/constants/l10n/langs/ko.js +1 -1
- package/dist/constants/l10n/langs/pl.js +1 -1
- package/dist/constants/l10n/langs/pt-BR.js +1 -1
- package/dist/constants/l10n/langs/pt.js +1 -1
- package/dist/constants/l10n/langs/ru.js +1 -1
- package/dist/constants/l10n/langs/uk.js +1 -1
- package/dist/constants/l10n/langs/zh.js +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/code-walkthrough/file-chunk-parser.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-link.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +5 -5
- package/dist/server/plugins/mcp/gateway-mcp/rate-limit/apply-edge-rate-limit.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/secret-store.d.ts +9 -3
- package/dist/server/plugins/mcp/gateway-mcp/secret-store.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/subject.js +1 -1
- package/dist/server/plugins/mcp/handlers/credentials-page/constants.d.ts +7 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/constants.js +1 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/form-contract.d.ts +5 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/form-contract.js +1 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/form-page.d.ts +14 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/form-page.js +62 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/layout.d.ts +17 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/layout.js +11 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/outcome-pages.d.ts +10 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/outcome-pages.js +11 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/site-branding.d.ts +10 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/site-branding.js +1 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/styles.d.ts +4 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/styles.js +186 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/types.d.ts +32 -0
- package/dist/server/plugins/mcp/handlers/credentials-page/types.js +0 -0
- package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.d.ts +0 -1
- package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +1 -1
- package/dist/server/plugins/mcp/index.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.js +2 -2
- package/dist/server/plugins/mcp/types.d.ts +6 -0
- package/dist/server/plugins/mcp/utils/mode.d.ts +4 -1
- package/dist/server/plugins/mcp/utils/mode.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/components.js +1 -1
- package/dist/server/providers/database/client.d.ts +5 -0
- package/dist/server/providers/database/client.js +1 -1
- package/dist/server/providers/database/copy-migrations.js +1 -1
- package/dist/server/providers/database/database-connections-manager.js +1 -1
- package/dist/server/providers/database/has-project-database.d.ts +3 -0
- package/dist/server/providers/database/has-project-database.js +1 -0
- package/dist/server/web-server/auth.d.ts +0 -1
- package/dist/server/web-server/auth.js +4 -4
- package/dist/server/web-server/jwt/token-types.d.ts +1 -0
- package/dist/server/web-server/jwt/token-types.js +1 -1
- package/dist/server/web-server/routes/catalog/helpers/create-entity-schema.d.ts +1756 -1756
- package/dist/server/web-server/routes/catalog/helpers/create-entity-update-schema.d.ts +295 -295
- package/dist/server/web-server/routes/mcp-routes/mcp-consent-page.d.ts +24 -0
- package/dist/server/web-server/routes/mcp-routes/mcp-consent-page.js +35 -0
- package/dist/server/web-server/routes/mcp-routes/mcp-oauth-store.d.ts +15 -0
- package/dist/server/web-server/routes/mcp-routes/mcp-oauth-store.js +1 -0
- package/dist/server/web-server/routes/mcp-routes/mcp-oauth.d.ts +3 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-oauth.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-routes.js +1 -1
- package/package.json +5 -5
- package/dist/server/plugins/mcp/handlers/mcp-credentials-page.d.ts +0 -12
- package/dist/server/plugins/mcp/handlers/mcp-credentials-page.js +0 -225
|
@@ -1 +1 @@
|
|
|
1
|
-
import{secretStore as
|
|
1
|
+
import{secretStore as d}from"../gateway-mcp/secret-store.js";import{rejectCredential as _}from"../gateway-mcp/credential-validation.js";import{buildAllowedHostMatchers as C}from"../gateway-mcp/fetch/allowed-hosts.js";import{assertUrlAllowed as A}from"../gateway-mcp/fetch/ssrf-guard.js";import{readCappedBody as T,getDefaultAgent as P}from"../gateway-mcp/fetch/fetch-bridge.js";import{logger as x}from"../../../tools/notifiers/logger.js";import{FORM_ACTIONS as k}from"./credentials-page/constants.js";import{credentialSchemesOf as w,schemeFieldName as f}from"./credentials-page/form-contract.js";import{formPage as N}from"./credentials-page/form-page.js";import{htmlDocument as O}from"./credentials-page/layout.js";import{declinedPage as R,expiredPage as I,outcomePage as j}from"./credentials-page/outcome-pages.js";const D={"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","X-Frame-Options":"DENY","Referrer-Policy":"no-referrer","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'; img-src 'self'; form-action 'self'; frame-ancestors 'none'; base-uri 'none'"},$=3e4,F=64*1024,M=/^bearer\s+/i,U=/^\d+$/,B=async(e,t,o)=>{const n=await t.getKv(),r=L(e,t.baseUrl,o);if(e.method==="GET"){const i=new URL(e.url).searchParams.get("token")??"",c=await d.peekElicitation(n,i);return c?S({kv:n,site:r,token:i,target:c},{},200):p(r)}if(e.method==="POST"){const i=await e.formData(),c=u(i,"token"),a=await d.peekElicitation(n,c);if(!a)return p(r);const s={kv:n,site:r,token:c,target:a,form:i};switch(u(i,"action")){case k.remove:return b(s,z(a,u(i,"scheme")));case k.removeAll:return b(s,w(a));case k.decline:return K(s);default:return H(s)}}return new Response("Method Not Allowed",{status:405,headers:{Allow:"GET, POST"}})};async function H(e){const{target:t,form:o}=e,n=w(t),r=n.map((s,l)=>u(o,f("client_id",l)).trim()),i=s=>S(e,{error:s,clientIds:r},400),c=X(n,o);if("error"in c)return i(c.error);if(c.writes.length===0)return i(G(n));const a=[];for(const s of c.writes){if(s.kind!=="exchange"){a.push(s);continue}const l=await J(s.auth,s.clientId,s.clientSecret);if(!l.ok)return x.warn(`gateway oauth2 client_credentials exchange failed for ${t.api}: ${l.reason}`),i(E(n,s.schemeIndex,"client_id","We could not exchange those client credentials for a token. Check the client ID and secret, then try again."));a.push({kind:"set",scheme:s.scheme,secret:l.secret})}return v(e,a)}function X(e,t){const o=[];for(const[n,r]of e.entries()){const i=u(t,f("secret",n)).trim();if(i){const s=_(r.auth,i);if(s)return{error:E(e,n,"secret",s)};o.push({kind:"set",scheme:r.name,secret:W(r.auth,i)});continue}if(r.auth.kind!=="oauth2ClientCredentials")continue;const c=u(t,f("client_id",n)).trim(),a=u(t,f("client_secret",n)).trim();if(c&&a)o.push({kind:"exchange",scheme:r.name,schemeIndex:n,auth:r.auth,clientId:c,clientSecret:a});else if(c||a)return{error:E(e,n,c?"client_secret":"client_id","Enter both the client ID and the client secret, or leave both blank.")}}return{writes:o}}function G(e){return e.length>1?{message:"Fill in at least one credential before saving."}:{message:e[0].auth.kind==="oauth2ClientCredentials"?"Enter an access token, or the client ID and the client secret, before saving.":"Enter a value before saving.",schemeIndex:0,field:"secret"}}function E(e,t,o,n){return{message:e.length>1?`${e[t].name}: ${n}`:n,schemeIndex:t,field:o}}async function b(e,t){const{kv:o,target:n}=e,r=await d.getStoredSchemes(o,n.subject,n.api),i=t.filter(c=>r[c.name]).map(c=>({kind:"remove",scheme:c.name}));return i.length===0?S(e,{error:{message:"No credential is stored there, so there is nothing to remove."}},400):v(e,i)}function z(e,t){const o=U.test(t)?w(e)[Number(t)]:void 0;return o?[o]:[]}async function K({kv:e,site:t,token:o}){return await d.consumeElicitation(e,o)?h(t,R(),200):p(t)}async function v({kv:e,site:t,token:o},n){const r=await d.consumeElicitation(e,o);if(!r)return p(t);for(const a of n)a.kind==="remove"?await d.deleteSecret(e,r.subject,r.api,a.scheme):await d.setSecret(e,r.subject,r.api,a.scheme,a.secret);const i=await d.getStoredSchemes(e,r.subject,r.api),c={action:n.some(a=>a.kind==="set")?"saved":"removed",schemes:n.map(a=>a.scheme),stored:Object.keys(i).sort()};return h(t,j(r,c),200)}async function S({kv:e,site:t,token:o,target:n},r,i){const c=await d.getStoredSchemes(e,n.subject,n.api);return h(t,N({site:t,target:n,token:o,stored:c,...r}),i)}function L(e,t,o){const n=o?.props?.branding??{},r=t&&URL.canParse(t)?t:e.url;return{...n,host:new URL(r).host}}async function J(e,t,o){try{const n=C([{url:e.tokenUrl}]),r=A(e.tokenUrl,n),i=new AbortController,c=setTimeout(()=>i.abort(),$);let a;try{a=await fetch(r,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:t,client_secret:o}),redirect:"manual",signal:i.signal,dispatcher:P()})}finally{clearTimeout(c)}if(!a.ok)return m(`token endpoint returned ${a.status}`);let s;try{s=await T(a,F)}catch{return m("token response exceeded the size cap")}let l;try{l=JSON.parse(s)}catch{return m("token response was not valid JSON")}const y=typeof l=="object"&&l!==null&&"access_token"in l?l.access_token:void 0,g=typeof y=="string"?y.trim():"";return g?_(e,g)?m("token response had an access_token that cannot be sent in a header"):{ok:!0,secret:{header:"Authorization",value:`Bearer ${g}`}}:m("token response had no access_token")}catch(n){return m(n instanceof Error?n.message:String(n))}}function m(e){return{ok:!1,reason:e}}function u(e,t){const o=e.get(t);return typeof o=="string"?o:""}function h(e,t,o){return new Response(O(t,e.colorScheme),{status:o,headers:D})}function p(e){return h(e,I(),404)}function W(e,t){return e.kind==="apiKey"?{header:e.header,value:t}:e.kind==="cookie"?{header:"Cookie",value:`${e.name}=${t}`}:e.kind==="bearer"||e.kind==="oauth2ClientCredentials"?{header:"Authorization",value:Y(t)}:{header:"Authorization",value:t}}function Y(e){return`Bearer ${e.replace(M,"")}`}var ie=B;export{ie as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fileURLToPath as U}from"node:url";import{dirname as $,join as
|
|
1
|
+
import{fileURLToPath as U}from"node:url";import{dirname as $,join as M}from"node:path";import{MCP_ROUTE as k,PUBLIC_MCP_ROUTE as b}from"@redocly/theme/core/constants/mcp";import{logger as p}from"../../tools/notifiers/logger.js";import{reporter as F}from"../../tools/notifiers/reporter.js";import{isDefined as B}from"../../../utils/guards/is-defined.js";import{MCP_CREDENTIALS_HANDLER_ID as P,MCP_CREDENTIALS_ROUTE as V,MCP_DOCS_SERVER_HANDLER_ID as T,MCP_NOT_ALLOWED_TEMPLATE_ID as x}from"../../constants/mcp.js";import{bundledOpenApiDocumentsToMcpOpenApi as J,bundledGraphqlDocumentsToMcpOpenApi as W,buildGraphqlDescriptions as X,collectIgnoredRoutes as z,filterIgnoredApiDescriptions as A,getCleanedUpApiDescriptions as K,isMcpInRedirects as Q}from"./utils.js";import{getMcpMode as R,isMcpElicitationEnabled as Y,isMcpGatewayEnabled as Z}from"./utils/mode.js";import{getPublicMcpEndpointStatus as _}from"./utils/public-endpoint.js";import{findGatedToolNames as ee}from"./utils/find-gated-tool-names.js";import{getDefaultToolSchemas as oe}from"./docs-mcp/tool-schemas.js";import{EXECUTE_TOOL_SCHEMA as te}from"./codemode/tools/execute-schema.js";import{isGatewayCapabilityEnabled as ie,isGatewayEligible as re}from"./gateway-mcp/eligibility.js";import{resolveGatewayFetchLimits as se}from"./gateway-mcp/rate-limit/limits.js";import{resetCustomMcpTools as ne,scanCustomMcpTools as ae}from"./custom-tools/scan-custom-tools.js";import{resolveSiteBranding as ce}from"./handlers/credentials-page/site-branding.js";import{telemetryTraceStep as pe}from"../../../cli/telemetry/helpers/trace-step.js";import{DEFAULT_DOCS_MCP_SERVER_NAME as S}from"./constants.js";const C=$(U(import.meta.url));async function de(){let i;const m={registeredToolNames:[],warnedNoOutputSchemaTools:new Set};let u=0,f=!1;return{id:"mcp",async processContent(e,a){await pe("com.redocly.build.plugin.mcp",async s=>{const t=await a.getConfig(),{mcp:r,redirects:g}=t;if(s?.setAttribute("config",JSON.stringify(r||{})),s?.setAttribute("mcpMode",R()),s?.setAttribute("mcpGateway",String(Z())),r?.hide||r?.docs?.hide){ne(e,m);return}Q(g)&&await F.panicOnBuildContentError('The reserved route "/mcp" cannot be used in redirect configuration. Please remove any redirects involving "/mcp" from your redocly.yaml config.'),e.createTemplate(x,"@redocly/theme/layouts/McpNotAllowed");const l=!!t?.access?.requiresLogin,h=t?.access?.rbac||{},d=R(),c=Y(),{budgetPerExec:D}=se();if(!i){p.info("Configuring MCP servers...");const w=J(await e.loadApiDocuments(a)),O=W(await e.loadApiDocuments(a)),v=r?.docs?.ignore||[],L=K(A(w,v)),q=ie(r),E=new Map;for(const o of L){const H=o.definition.info?.title||"",j=o.definition.info?.version||"";E.set(o.relativePath,{name:H,description:typeof o.definition.info?.description=="string"?o.definition.info.description:"",version:j,servers:o.definition.servers||[],relativePath:o.relativePath,...q&&re(o.definition.info,o.relativePath,h)?{fetchEligible:!0}:{}})}const y=X(A(O,v)),G=Object.values(t.products||{}).map(o=>o?.name).filter(B),I=t.l10n?.locales?.map(o=>o.code)??a.fs.localeFolders,N=oe({products:G,locales:I,requiresLogin:l,rbac:h,hasGraphqlApis:y.length>0,elicitationEnabled:c,hasCallableApis:E.values().some(o=>o.fetchEligible),feedbackHidden:t.feedback?.hide===!0});e.addMcpTools(M(C,"./docs-mcp/tools/index.js"),N),d==="code"&&e.addMcpTools(M(C,"./codemode/tools/execute.js"),[te]),i={apiDescriptionsMap:Object.fromEntries(E),graphqlDescriptions:y}}const n=await ae(e,a,m);s?.setAttribute("customToolsCount",String(n)),n!==u&&(u=n,n>0&&p.info(`Registered ${n} custom MCP tool${n===1?"":"s"} from @mcp folders`)),await le({actions:e,handlerPath:M(C,"./handlers/docs-mcp-handler.js"),apiDescriptionsMap:i.apiDescriptionsMap,graphqlDescriptions:i.graphqlDescriptions,mcpConfig:r,mcpMode:d,elicitationEnabled:c,gatewayFetchBudgetPerExec:D,publicEndpointStatus:_(t)}),f||(p.info("Registered Docs MCP Server endpoint at /mcp"),f=!0)})},async afterRoutesCreated(e,a){const s=await a.getConfig(),{mcp:t}=s,r=!t?.hide&&!t?.docs?.hide;e.markGatedMcpTools(await ee(e.getMcpTools())),e.setGlobalData({mcpData:{docs:{enabled:r,name:t?.docs?.name||S,publicEndpoint:_(s)==="served"}}})}}}var Oe=de;async function le({actions:i,handlerPath:m,apiDescriptionsMap:u,graphqlDescriptions:f,mcpConfig:e,mcpMode:a,elicitationEnabled:s,gatewayFetchBudgetPerExec:t,publicEndpointStatus:r}){i.createRequestHandler(T,m);const g=l=>async(h,d)=>{const c=z(d.getAllRoutes(),e?.docs?.ignore||[]);return c.length>0&&p.verbose(`mcp.docs.ignore: hiding ${c.length} page routes from MCP search`),{props:{config:{apiDescriptionsMap:u,graphqlDescriptions:f,mcpDocsServerName:e?.docs?.name||S,mcpMode:a,elicitationEnabled:s,gatewayFetchBudgetPerExec:t,searchIgnoredRoutes:c,...l?{isPublicEndpoint:l}:{}},serverOutDir:i.serverOutDir,tools:i.getMcpTools().map(({importPath:D,...n})=>n)}}};if(i.addApiRoute({slug:k,requestHandlerId:T,httpMethod:"all",getStaticData:g(!1)}),r==="no-protected-content"?p.info(`mcp.docs.publicEndpoint is enabled, but the project has no protected content - /mcp already serves anonymous users, skipping ${b}.`):r==="anonymous-denied"?p.info(`mcp.docs.publicEndpoint is enabled, but anonymous users are denied the MCP feature (rbac.features.mcp or requiresLogin) - skipping ${b}.`):r==="served"&&(i.addApiRoute({slug:b,requestHandlerId:T,httpMethod:"all",getStaticData:g(!0)}),p.verbose(`Registered public Docs MCP Server endpoint at ${b}`)),s){const l=M(C,"./handlers/mcp-credentials-handler.js");i.createRequestHandler(P,l),i.addApiRoute({slug:V,requestHandlerId:P,httpMethod:"all",getStaticData:async(h,d)=>{const{seo:c,colorMode:D}=d.getConfig(),{logo:n}=d.getGlobalData();return{props:{branding:ce({seo:c,logo:n,colorMode:D})}}}}),p.verbose("Registered MCP credentials endpoint at /mcp/credentials")}}export{Oe as default,de as mcpServerPlugin};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{McpServer as C}from"@modelcontextprotocol/server";import{logger as S}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as
|
|
1
|
+
import{McpServer as C}from"@modelcontextprotocol/server";import{logger as S}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as _,MCP_TOOL_WORKER_KEY as O}from"../../../workers/mcp-tool-worker-pool.js";import{secretStore as L}from"../gateway-mcp/secret-store.js";import{readAuthRequiredMeta as D,stripAuthRequiredMeta as E,upstreamAuthKey as b}from"../gateway-mcp/upstream-auth.js";import{acceptedCredentialsLink as K,buildCredentialsAcceptedHint as j,buildCredentialsToolHint as F,buildUpstreamCredentialFallback as q,buildUpstreamCredentialInputRequired as v,credentialElicitationAnswer as H,dedupeElicitation as N,isStatelessRequest as P,schemeLabelOf as $,supportsUrlElicitation as B}from"../gateway-mcp/elicitation.js";import{DEFAULT_SCHEME_NAME as y,resolveUpstreamApiAuth as G}from"../gateway-mcp/upstream-auth-scheme.js";import{MISSING_SESSION_HANDLE_MESSAGE as V,resolveSecretSubject as W}from"../gateway-mcp/subject.js";const Y=6e4;function ue(e,t){return new C({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}},...t?{instructions:t}:{}})}function le(e,t){return r=>async(n,s)=>{S.info(`MCP tool called: ${r}`);const i=e.elicitationEnabled??!1,u=P(s.mcpReq),a=i?W(e.user,n.sessionHandle):{kind:"anonymous"},o=a.kind==="resolved"?a.subject:void 0;if(a.kind==="invalid")return{content:[{type:"text",text:a.message}],isError:!0};const c=async()=>{if(!(!i||!o||!t?.getKv))try{const m=await t.getKv();return await L.getSecretsForSubject(m,o)}catch(m){S.warn(`gateway mcp secret store error, continuing unauthenticated: ${String(m)}`);return}};let l;const d=async()=>{l=await c();const m={toolName:r,args:n,context:{...e,upstreamSecrets:l},extra:ee(s)};return await _.exec(O,[m],{timeout:Y})};let p=await d();const f=D(p);if(f.length>0&&i&&!o)return E(I(p,[{type:"text",text:V}]));if(f.length>0&&i&&o&&t?.getKv){S.info(`gateway mcp credentials prompt: era=${u?"2026-07-28":"2025"} clientElicitation=${J(t)}`);const m=await t.getKv(),h=await Q(e,f),w=f.map(U=>X(U,h)),k=Z(l),g=u?await x(e,s,w,o,m,k,h,t):await z(e,w,o,m,k,h);switch(g.kind){case"inputRequired":return g.result;case"retry":p=await d();break;case"prependText":p=I(p,[{type:"text",text:g.text},...p.content]);break;case"none":break}}return E(p)}}function I(e,t){const{structuredContent:r,...n}=e;return{...n,content:t,isError:!0}}function J(e){const t=e?.getClientCapabilities()?.elicitation;if(!t)return"none";const r=[t.form?"form":void 0,t.url?"url":void 0].filter(Boolean);return r.length>0?r.join("+"):"none"}async function Q(e,t){const r=new Map;for(const n of new Set(t.map(s=>s.api)))r.set(n,await G(e,n));return r}function X(e,t){const r=t.get(e.api);if(!r)return{...e,scheme:e.scheme??y};const n=e.scheme&&r.schemes[e.scheme]?e.scheme:r.defaultScheme;return{...e,scheme:n}}function Z(e){const t=new Set;for(const[r,n]of Object.entries(e??{}))for(const s of Object.keys(n))t.add(b({api:r,scheme:s}));return t}function R(e,t){return e.status===401&&!t.has(b(e))}function T(e,t){const r=t.get(e.api),n=e.sentScheme??e.scheme;return r&&n?$(r.schemes,n):void 0}function M(e,t){return e.map(r=>F(r,T(r,t))).join(`
|
|
2
2
|
|
|
3
|
-
`)}function A(e,t,r,n,s){const[i,...
|
|
3
|
+
`)}function A(e,t,r,n,s){const[i,...u]=t,a=s.get(i.api),o=i.scheme??a?.defaultScheme??y,c=i.schemes&&i.schemes.length>1?i.schemes:void 0,l=c?void 0:T(i,s);return{subject:r,baseUrl:e.baseUrl??"",target:{api:i.api,name:i.name,scheme:o,...l?{schemeLabel:l}:{},...c?{acceptedSchemes:c}:{}},auth:a?.schemes[o]??{kind:"raw"},...a?{apiAuth:a}:{},kv:n,hasExistingSecret:!1,reason:{kind:"authFailure",status:i.status,...i.detail?{detail:i.detail}:{},otherApiNames:u.map(d=>d.name)}}}async function x(e,t,r,n,s,i,u,a){const o=H(t.mcpReq.inputResponses);if(o==="declined")return{kind:"none"};const c=r.filter(d=>R(d,i));if(c.length===0)return{kind:"prependText",text:M(r,u)};if(o==="accepted"){const d=await K(t.mcpReq.requestState(),{kv:s,subject:n,baseUrl:e.baseUrl??"",apis:c.map(f=>f.api)}),p=d?.apiName??c[0].name;return{kind:"prependText",text:j(p,d?.url)}}const l=A(e,c,n,s,u);return B(a?.getClientCapabilities())?{kind:"inputRequired",result:await v(l)}:{kind:"prependText",text:await q(l)}}async function z(e,t,r,n,s,i){const u=t.filter(c=>R(c,s));if(u.length===0)return{kind:"prependText",text:M(t,i)};const a=A(e,u,r,n,i);return{kind:"prependText",text:await N(`${r}:${b(u[0])}`,()=>q(a))}}function ee(e){return{sessionId:e.sessionId,authInfo:e.http?.authInfo,requestId:e.mcpReq.id,_meta:e.mcpReq._meta}}function de(e){return{user:e.user,config:e.config,outdir:e.outdir,baseUrl:e.baseUrl,params:e.params,query:e.query,cookies:e.cookies,apiDescriptionsMap:e.apiDescriptionsMap,graphqlDescriptions:e.graphqlDescriptions,products:e.products,accessToken:e.accessToken,metadata:e.metadata,upstreamAuthorization:e.upstreamAuthorization,mcpMode:e.mcpMode,elicitationEnabled:e.elicitationEnabled,gatewayFetchBudgetPerExec:e.gatewayFetchBudgetPerExec,clientIp:e.clientIp,client:e.client,clientVersion:e.clientVersion,requestId:e.requestId,messageId:e.messageId,upstreamSecrets:e.upstreamSecrets,searchIgnoredRoutes:e.searchIgnoredRoutes,internalBaseUrl:e.internalBaseUrl,sandboxToolSchemas:e.sandboxToolSchemas,fs:void 0}}export{ue as createMcpServer,le as createToolDispatch,de as extractSerializableContext};
|
|
@@ -5,6 +5,7 @@ import type { McpErrorCodes, McpServerType } from './constants';
|
|
|
5
5
|
import type { McpToolSchema } from '../../types';
|
|
6
6
|
import type { Skill } from '../../types/plugins/skills.js';
|
|
7
7
|
import type { UpstreamSecrets } from './gateway-mcp/secret-store.js';
|
|
8
|
+
import type { SiteBranding } from './handlers/credentials-page/types.js';
|
|
8
9
|
type ObjectValues<T> = T[keyof T];
|
|
9
10
|
export type McpServerType = ObjectValues<typeof McpServerType>;
|
|
10
11
|
export type McpErrorCodeTypes = ObjectValues<typeof McpErrorCodes>;
|
|
@@ -40,6 +41,11 @@ export type McpDocsStaticData = PageStaticData & {
|
|
|
40
41
|
skillsBaseUrl?: string;
|
|
41
42
|
};
|
|
42
43
|
};
|
|
44
|
+
export type CredentialsPageStaticData = PageStaticData & {
|
|
45
|
+
props?: {
|
|
46
|
+
branding?: SiteBranding;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
43
49
|
export type SkillMcpResource = {
|
|
44
50
|
slug: string;
|
|
45
51
|
name: string;
|
|
@@ -10,7 +10,10 @@ import type { McpMode } from '../types.js';
|
|
|
10
10
|
* data / McpToolContext instead (see docs-mcp-handler / execute-mcp-tool).
|
|
11
11
|
*/
|
|
12
12
|
export declare function isMcpCodeModeEnabled(): boolean;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* The gateway capability: a guarded `fetch` in the `execute` sandbox. Requires code mode and a
|
|
15
|
+
* project database.
|
|
16
|
+
*/
|
|
14
17
|
export declare function isMcpGatewayEnabled(): boolean;
|
|
15
18
|
export declare function isMcpElicitationEnabled(): boolean;
|
|
16
19
|
export declare function getMcpMode(): McpMode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{EntitlementsProvider as e}from"../../../entitlements/entitlements-provider.js";function t(){return e.instance().entitlements?.mcpMode==="code"}function
|
|
1
|
+
import{EntitlementsProvider as e}from"../../../entitlements/entitlements-provider.js";import{hasProjectDatabase as n}from"../../../providers/database/has-project-database.js";function t(){return e.instance().entitlements?.mcpMode==="code"}function o(){return!!e.instance().canAccessFeature("mcpGateway")&&t()&&n()}function i(){return o()}function a(){return t()?"code":"tools"}export{a as getMcpMode,t as isMcpCodeModeEnabled,i as isMcpElicitationEnabled,o as isMcpGatewayEnabled};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as e from"react";import{useState as i}from"react";import{getScorecardColorVariable as s}from"@redocly/theme/core/utils";import{Button as m}from"@redocly/theme/components/Button/Button";import{SubtractIcon as u}from"@redocly/theme/icons/SubtractIcon/SubtractIcon";import{AddIcon as p}from"@redocly/theme/icons/AddIcon/AddIcon";import{Gauge as E}from"./Gauge";import{Code as f,Dot as o,FilePreview as d,InstanceControlsWrap as v,InstanceMessage as x,InstanceReference as C,InstanceWrap as I,ContentWithDotWrap as a,EmojiWrap as b}from"./index.styles";import{SEVERITY_TO_COLOR as g,severityColor as S,severityIcon as R}from"./utils";function D({level:t,colorVariable:r}){return e.createElement(a,null,e.createElement(o,{colorVariable:r}),e.createElement("span",null,t))}function L({summary:t}){const r=Object.keys(t.levels).length;return e.createElement(E,{chunks:Object.entries(t.levels).map(([l,n],c)=>({share:n/t.total*100,colorVariable:s(c,r),title:`${n} ${l}`}))})}function P({severity:t}){return e.createElement(o,{colorVariable:g[t]})}function A({errors:t,warnings:r}){return e.createElement(a,null,e.createElement(o,{colorVariable:`--scorecard-color-${t>0?"error":r>0?"warning":"success"}`}),e.createElement("span",null,t>0?"Problem":r>0?"Warning":"Normal"))}function N({errors:t,warnings:r}){return e.createElement(o,{colorVariable:`--scorecard-color-${t>0?"error":r>0?"warning":"success"}`})}function V({problem:t}){const[r,l]=i(!1);return e.createElement(v,{onClick:()=>l(n=>!n),className:r?"expanded":""},e.createElement(m,{variant:"text",size:"medium",icon:r?e.createElement(u,null):e.createElement(p,null)}),e.createElement(I,{borderColor:S(t.severity)},e.createElement("div",null,e.createElement(x,null,e.createElement(b,null,R(t.severity))," ",t.message)),r?e.createElement(d,{onClick:n=>n.stopPropagation()},e.createElement(C,null,e.createElement("strong",null,t.location.source.absoluteRef),t.location.pointer),e.createElement(f,null,t.codeframe)):null))}const T=t=>e.createElement(V,{problem:t});export{D as ApiLevel,V as Instance,P as InstanceStatus,N as LevelStatus,A as RuleStatus,L as StatusDistribution,T as renderRowSubComponent};
|
|
@@ -17,6 +17,11 @@ export declare class DatabaseClient {
|
|
|
17
17
|
static init(config: Config, options?: DatabaseClientInitOptions): Promise<DatabaseClient>;
|
|
18
18
|
migrate(prefix: string, suffix?: string): Promise<void>;
|
|
19
19
|
close(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Closes the client during error handling. Never throws, so the caller can rethrow
|
|
22
|
+
* the original error; a close failure is logged.
|
|
23
|
+
*/
|
|
24
|
+
closeAfterFailure(): Promise<void>;
|
|
20
25
|
sync(): Promise<void>;
|
|
21
26
|
}
|
|
22
27
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import o from"node:path";import{sql as c}from"drizzle-orm";import{drizzle as l}from"drizzle-orm/libsql";import{migrate as m}from"drizzle-orm/libsql/migrator";import{logger as d}from"../../tools/notifiers/logger.js";import{TransactionsManager as h}from"./transactions-manager.js";class n{dbClient;#t;constructor(t,r){this.dbClient=l(r(t)),this.#t=new h(this.dbClient)}get transactionsManager(){return this.#t}get client(){return this.#t.tx??this.dbClient}static async init(t,r){const{createClient:a}=await import("@libsql/client"),i=new n(t,a);if(r?.runWithPragmaWalWriteOptimization)try{await i.dbClient.run(c.raw("PRAGMA journal_mode = WAL;"))}catch(s){throw await i.closeAfterFailure(),s}return i}async migrate(t,r=""){await m(this.dbClient,{migrationsFolder:o.join(t,"migrations",r)})}async close(){this.dbClient.$client.close()}async closeAfterFailure(){try{await this.close()}catch(t){d.warn("Failed to close the database client: %s",t)}}async sync(){try{await this.dbClient.$client.sync()}catch(t){if(u(t))return;throw t}}}function u(e){if(!(e instanceof Error))return!1;const t=e.message.toLowerCase();return t.includes("syncnotsupported")&&(t.includes('("file")')||t.includes('"file"'))}export{n as DatabaseClient};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import n from"node:path";import{cpSync as m,existsSync as i,mkdirSync as c}from"node:fs";import{logger as e}from"../../tools/notifiers/logger.js";import{fromCurrentDir as u}from"../../utils/index.js";import{hasProjectDatabase as f}from"./has-project-database.js";const D=r=>{if(!f())return;const o="sqlite-db",t=u(import.meta.url,`./databases/${o}/migrations`),a=n.join(r.serverOutDir,"migrations",o);if(!i(t)){e.warn(`Migrations folder not found at ${t}`);return}i(r.serverOutDir)||c(r.serverOutDir,{recursive:!0});try{m(t,a,{recursive:!0})}catch(s){e.error("Failed to copy migrations folder: %s",s)}};export{D as copyMigrationsFolder};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as c}from"../../tools/notifiers/logger.js";import{SqldNotRunningError as y}from"./sqld-not-running-error.js";import{retryAsyncOperation as
|
|
1
|
+
import{logger as c}from"../../tools/notifiers/logger.js";import{SqldNotRunningError as y}from"./sqld-not-running-error.js";import{retryAsyncOperation as h}from"./utils/retry-async-operation.js";import{DatabasePathGenerator as w}from"./database-path-generator.js";import{DatabaseClient as g}from"./client.js";import{describeTransientError as z}from"./transient-sqld-error.js";const u="_redocly_db_preconnect_cache_";class i{static get#t(){const t=globalThis;if(!t[u]){const r={connections:new Map,pendingConnections:new Map,failedConnections:new Set};t[u]=r}return t[u]}static get#i(){return i.#t.connections}static get#e(){return i.#t.failedConnections}static get#r(){return i.#t.pendingConnections}static async getLocalConnection(t){return i.#n(t,async()=>i.#a(t))}static async getRemoteConnection(t){return i.#n(t,async()=>i.#d(t))}static async#n(t,r){const e=i.#o(t),n=i.#i.get(e);if(n)if(n.client.dbClient.$client.closed)c.warn("Cached database client is closed. Reinitializing connection..."),i.#i.delete(e);else return n;return i.#l({key:e,initializer:r})}static#o=t=>{const r="syncUrl"in t&&typeof t.syncUrl=="string"?`|${t.syncUrl}`:"",e=t.additionalFolder??"";return`${t.baseDbDir}|${e}|${t.databaseName}${r}`};static async#l({key:t,initializer:r}){let e=i.#r.get(t);e||(i.#e.has(t)&&c.warn("Error connecting to the database, retrying..."),e=i.#s(t,r),i.#r.set(t,e));try{return await e}catch(n){return c.error("Error connecting to the database: %s",z(n)),i.#e.add(t),null}finally{i.#r.delete(t)}}static async#s(t,r){const e=await r();return i.#i.set(t,e),i.#e.delete(t),e}static async#a({removeExisting:t,baseDbDir:r,runWithPragmaWalWriteOptimization:e,databaseName:n,additionalFolder:a,migrationsFolder:d}){c.info("Initializing local database client...");const l=c.startTiming(),s=await i.#c({removeExisting:t,baseDbDir:r,databaseName:n,additionalFolder:a,migrationsFolder:d,runWithPragmaWalWriteOptimization:e});return c.infoTime(l,"Local database client initialized"),s}static async#d({removeExisting:t,baseDbDir:r,syncUrl:e,authToken:n,databaseName:a,additionalFolder:d,migrationsFolder:l}){c.info("Initializing remote database client...");const s=c.startTiming();return await h(async()=>{try{const o=await i.#c({removeExisting:t,baseDbDir:r,databaseName:a,additionalFolder:d,migrationsFolder:l,syncUrl:e,authToken:n});return c.infoTime(s,"Remote database client initialized"),o}catch(o){throw y.isSqldNotRunningError(o)?new y("SQLD server is not running yet, wait for production deployment",{cause:o}):o}},{maxRetries:3,retryStrategy:"exponential",delay:1e3,logger:c,messages:{retry:"Retrying remote database client initialization",maxRetriesReached:"Max retries reached, remote database client initialization failed"}})}static async#c({removeExisting:t,baseDbDir:r,databaseName:e,additionalFolder:n,migrationsFolder:a,runWithPragmaWalWriteOptimization:d,syncUrl:l,authToken:s}){const{databasePath:o}=await w.prepareDatabasePath({baseDir:r,databaseName:e,additionalFolder:n,removeExisting:t}),p=l&&s?{url:`file:${o}`,syncUrl:l,authToken:s}:{url:`file:${o}`},m=await g.init(p,{runWithPragmaWalWriteOptimization:d});try{await m.migrate(r,a)}catch(f){throw await m.closeAfterFailure(),f}return{client:m,path:o}}}export{i as DatabaseConnectionsManager};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{PACKAGE_NAME as e}from"../../../config/product-gates.js";const r=["@redocly/realm","@redocly/reef","@redocly/redoc-reef","@redocly/revel-reef"];function c(){return r.includes(e)}export{c as hasProjectDatabase};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import"../node-crypto-polyfill.js";import{DOMParser as
|
|
1
|
+
import"../node-crypto-polyfill.js";import{DOMParser as j}from"@xmldom/xmldom";import{SignedXml as Z}from"xml-crypto";import ee from"xpath";import{deflateSync as te,inflateSync as ne}from"fflate";import{createHash as re}from"crypto";import{ulid as oe}from"ulid";import{AuthProviderType as f,DEFAULT_TEAM_CLAIM_NAME as U}from"@redocly/config";import{ALTERNATIVE_AUD_CLAIM_NAME as se,AUTH_URL as ie,JWT_SECRET_KEY as y,MAX_AUTH_TOKEN_COOKIE_BYTES as ae}from"../constants/common.js";import{ensureArray as R}from"../../utils/array/ensure-array.js";import{envConfig as ce}from"../config/env-config.js";import{decryptClaimValue as ue}from"../utils/crypto/decrypt-claim-value.js";import{decryptAesGcm as de}from"../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as le}from"../utils/crypto/encrypt-aes-gcm.js";import{getPathPrefix as me,withPathPrefix as pe,withoutPathPrefix as fe}from"@redocly/theme/core/utils";import{ServerRoutes as V}from"../../constants/api.js";import{AuthCookieNames as I,DEFAULT_AUTHENTICATED_TEAM as he,REQUIRED_OIDC_SCOPES as B}from"../../constants/common.js";import{appendQueryParams as ye}from"../../utils/url/append-query-params.js";import{parsePreviewUrl as $}from"../../utils/url/parse-preview-url.js";import{logger as O}from"../tools/notifiers/logger.js";import{randomString as Ae}from"../utils/crypto/random-string.js";import{randomUUID as M}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as d,JwtTokenExpired as we}from"./jwt/types.js";import*as m from"./jwt/jwt.js";import{InternalTokenTypes as T}from"./jwt/token-types.js";import{parseTeamClaimToArray as _e}from"../utils/index.js";import{parseTeamNameTemplate as Se}from"../utils/rbac-expand.js";import{arrayBufferToBase64 as Te,decodeBase64 as F,encodeBase64URL as xe,urlSafeBase64 as H}from"./jwt/encode.js";import{formatSamlCertificate as ge}from"./utils/format-saml-certificate.js";const Ie=new Set(Object.values(T));function x(e){return e?.type===f.OIDC}function Ee(e){return e?.type===f.SAML2}async function Rt(e,t){if(x(t))return ke(e,t);if(Ee(t))return Re(e,t)}async function ke(e,t){const n=await G(e,t),r=new Set((t.scopes||[]).concat(B)),o=t.authorizationRequestCustomParams||{};return{type:f.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:o,pkce:t.pkce}}function Re(e,t){return{type:f.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}function K(e){return Array.from(new Set((e.scopes||[]).concat(B))).join(" ")}async function z(e,t,n){return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:t.clientId,...t.clientSecret?{client_secret:t.clientSecret}:{},...n}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(r=>r.json())}async function Ot(e,t,n,r,o={}){return await z(e,r,{scope:K(r),code:t,redirect_uri:J(n),grant_type:"authorization_code",...o})}async function Mt(e,t,n){return await z(e,n,{scope:K(n),grant_type:"refresh_token",refresh_token:t})}function Oe(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:o,extraParams:s,idpId:i,pkce:c},u,h,p){if(!t||!n||!r||!o)return{loginUrl:void 0};const a=new URL(t),w=p?.redirectUriOverride??`${e}${pe(V.OIDC_CALLBACK)}`,_={state:M(),idpId:i,redirectUri:w,redirectTo:u,branch:p?.branchOverride??Ce(e),inviteCode:h,source:p?.sourceOverride??"portal",uiLocales:p?.uiLocales},S={};if(c){const A=H(Ae(50)),E=H(re("sha256").update(A).digest("base64")),k="S256";a.searchParams.append("code_challenge",E),a.searchParams.append("code_challenge_method",k),S.code_verifier={value:A,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:me()||"/"}}}a.searchParams.append("client_id",n),a.searchParams.append("scope",o),a.searchParams.append("response_type",r),a.searchParams.append("redirect_uri",J(w)),a.searchParams.append("state",xe(JSON.stringify(_))),p?.uiLocales&&a.searchParams.append("ui_locales",p.uiLocales);for(const A in s)s[A]!==void 0&&a.searchParams.append(A,s[A]);return{loginUrl:a.toString(),cookies:S}}function Lt(e,t,n,r){const o=new URL(e);return n&&(o.searchParams.append("post_logout_redirect_uri",t),r&&o.searchParams.append("state",r),o.searchParams.append("id_token_hint",n)),o.toString()}function Me(e,t){const n=R(t?.audience??[]);return!n.length||W(e,n)}function W(e,t){const n=[e.aud,e[se]].flat();return t.some(r=>r!==""&&n.includes(r))}function Le(e,t){return!t||!x(t)?!0:Me(e,t)&&Ne(e,t)}function Ne(e,t){return!x(t)||!We(t)||!t.clientId?!0:R(e.aud||[]).includes(t.clientId)}function Nt(e){if(!e)return!1;try{return m.decode(e).header.alg===d.RS256}catch{return!1}}async function Ct(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return m.sign({type:T.MCP_AUTHORIZATION_CODE,client_id:e.clientId,redirect_uri:e.redirectUri,id_token_enc:le(e.idToken,y),...e.idpId?{idp_id:e.idpId}:{},...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},...e.codeChallenge?{code_challenge:e.codeChallenge}:{},...e.codeChallengeMethod?{code_challenge_method:e.codeChallengeMethod}:{},iat:t,exp:n},y,d.HS256)}async function Dt(e){await m.verify(e,y,d.HS256);const{payload:t}=m.decode(e);if(t.type!==T.MCP_AUTHORIZATION_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");if(typeof t.id_token_enc!="string")throw new Error("Authorization code missing session");return{...t,id_token:de(t.id_token_enc,y)}}async function bt(e){const t=Math.floor(Date.now()/1e3);return m.sign({type:T.MCP_REFRESH_TOKEN,client_id:e.clientId,...e.idpId?{idp_id:e.idpId}:{},claims:e.claims,...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},session_exp:e.sessionExp,iat:t,exp:e.sessionExp,jti:M()},y,d.HS256)}async function Pt(e){await m.verify(e,y,d.HS256);const{payload:t}=m.decode(e);if(t.type!==T.MCP_REFRESH_TOKEN)throw new Error("Invalid refresh token type");if(!t.client_id||typeof t.claims!="object"||t.claims===null)throw new Error("Refresh token missing required claims");if(typeof t.session_exp!="number")throw new Error("Refresh token missing session expiry");return t}function vt(e){const t=e||oe(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcpSession",uri:`urn:redocly:realm:mcp:session:${n}`}}function J(e){const t=$(e);if(!t)return e;const n=De(t.hostname);if(n)return t.hostname=`previewauth--${n.previewBranch}${n.after}`,q(e,t);const r=Y(t.hostname);return r?(t.hostname=`${r.projectSlug}.previewauth.${r.after}`,q(e,t)):e}function Ce(e){const t=$(e);if(t)return Y(t.hostname)?.previewBranch}function q(e,t){const n=e.replace(/^https?:\/\/[^/?#]+/i,"");return`${t.protocol}//${t.host}${fe(n)}`}function Y(e){const t=e.split(".preview.",2);if(t.length<2)return null;const[n,r]=t,o=n.indexOf("--");if(o===-1)return null;const s=n.slice(0,o),i=n.slice(o+2);return!s||!i||i.includes(".")?null:{projectSlug:s,previewBranch:i,after:r}}function De(e){const t=e.indexOf("."),n=t===-1?e:e.slice(0,t);if(!n.startsWith("preview-"))return null;const r=n.indexOf("--");if(r===-1)return null;const o=n.slice(r+2);if(!o)return null;const s=t===-1?"":e.slice(t);return{previewBranch:o,after:s}}function be(e){return e.type===f.OIDC}function Pe(e){return e.type===f.SAML2}function jt(e,t,n,r,o){return be(e)?Oe(t,e,n,r,{uiLocales:o}):Pe(e)?ve(t,e,n,r,o):{}}function ve(e,t,n,r,o){const i=`<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="_${M()}"
|
|
5
5
|
IssueInstant="${new Date().toISOString()}"
|
|
6
|
-
AssertionConsumerServiceURL="${e}${
|
|
6
|
+
AssertionConsumerServiceURL="${e}${V.SAML_CALLBACK}"
|
|
7
7
|
AttributeConsumingServiceIndex="0">
|
|
8
8
|
<saml:Issuer>${t.entityId}</saml:Issuer>
|
|
9
9
|
<samlp:NameIDPolicy
|
|
10
10
|
AllowCreate="true"
|
|
11
11
|
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
|
|
12
|
-
</samlp:AuthnRequest>`,c=
|
|
12
|
+
</samlp:AuthnRequest>`,c=je(i);return{loginUrl:ye(t.ssoUrl,{SAMLRequest:c,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function je(e){return Te(te(new TextEncoder().encode(e)).buffer)}function Ut(e){const t=F(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=ne(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function Vt(e){try{return JSON.parse(F(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function Bt(e){const t=new j().parseFromString(e,"application/xml"),r=l(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,s=l(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",i=l(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],c=i&&i.nodeValue||void 0,u=l(t,"//*[local-name(.)='Audience']/text()")[0],h=u&&u.nodeValue||void 0,a=l(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",w=l(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],_=w&&w.nodeValue||"",S=l(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],A=S&&S.nodeValue||"",E=l(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],k=Ue(E),C={},D=l(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(D.length)for(const b of D){const P=l(b,"./@Name")[0];if(P.nodeValue){const v=l(b,"./*[local-name(.)='AttributeValue']/text()")[0];v?.nodeValue&&(C[P.nodeValue]=v.nodeValue)}}return{uid:_,success:r,expiresAt:k,issuerId:c,entityId:h,attrs:C,cert:a,nameFormat:A,destination:s}}function Ue(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 N={},g={jwks:{}};async function G(e,t){if(!N[e]){const n=t.configurationUrl?await Q(t.configurationUrl):t.configuration;N[e]=Ve()?$e(n):n}return N[e]}function Ve(){const e=ce.REDOCLY_ENFORCE_RESIDENCY;return!!e&&e.includes("host.docker.internal")}const Be=["authorization_endpoint","end_session_endpoint"];function $e(e){if(typeof e!="object"||e===null)return e;const t={...e};for(const n of Object.keys(t)){if(Be.includes(String(n)))continue;const r=t[n];typeof r=="string"&&r.includes("://localhost")&&(t[n]=r.replace("://localhost","://host.docker.internal"))}return t}const Fe=["kty","n","e","crv","x","y"];function He(e,t){return Fe.every(n=>e[n]===t[n])}async function Ke(e){const t={};for(const n of Object.keys(e)){const r=e[n];if(!x(r))continue;const o=await G(n,r);if(o.jwks_uri){const s=await Q(o.jwks_uri);for(const i of s.keys){const c=t[i.kid],u=c&&He(c,i)?[...c.idpIds,n]:[n];t[i.kid]={...i,idpIds:u},g.jwks[i.kid]=t[i.kid]}}}}function ze(e,t,n){const r=g.jwks[e]?.idpIds??[];return r.find(o=>{const s=n[o];if(!x(s))return!1;const i=R(s.audience??[]);return W(t,i.length?i:[s.clientId])})??r.at(-1)}async function Q(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function $t(e){return fetch(`${ie}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function We(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"].some(r=>Je(t.hostname,r))}function Je(e,t){return e===t||e.endsWith(`.${t}`)}async function Ft(e,t){const n=new j().parseFromString(e,"application/xml"),r=l(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 o=ge(t),s=new Z({publicCert:o});s.loadSignature(r);try{return s.checkSignature(e)}catch{return!1}}function qe(e){const t=new Set,n=r=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[o,s]of Object.entries(r))typeof s=="string"?t.add(o):n(s)};return n(e),t}function Ye(e,t,n){if(!t)return e;const r=qe(t),o=s=>{const i=n?.[s]||s;return r.has(i)||!!Se(t,i)};return[...e.filter(o),...e.filter(s=>!o(s))]}async function Ht(e,t,n,r){const o=n?e[n]:void 0,s=Array.isArray(o)?Ye(o.map(String),r?.rbacConfig,r?.teamsMapping):[];let i=s.length;const c=s.length+1;for(let u=0;u<c;u++){const h={...e,idpId:t};n&&s.length&&(h[n]=s.slice(0,i));const p=await m.sign(h,y,d.HS256),a=Buffer.byteLength(p)-ae;if(a<=0)return{token:p,teamsTotal:s.length,teamsKept:i,droppedTeams:s.slice(i)};if(i===0)return null;const w=Buffer.byteLength(JSON.stringify(s.slice(0,i))),_=Math.max(1,w/i*4/3);i=Math.max(0,i-Math.max(1,Math.ceil(a/_)))}return null}function Kt(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 o;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(o=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/^.+@.+/m)&&(o=e);const s=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],i=s?.match(/^.+@.+/m);return o=o||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(i?s:void 0),o=o?.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"]||s,email:o,email_verified:!0,teams:r?_e(n[r]):[]}}function X(e,t={}){return e.map(n=>t[n]||n)}async function zt(e,t){if(!t)return{};const n=t[I.AUTHORIZATION];if(!n)return{};try{const r=m.decode(n);if(typeof r.payload.type=="string"&&Ie.has(r.payload.type))return{};if(r.header.alg===d.RS256){g.jwks[r.header.kid]===void 0&&await Ke(e);const h=g.jwks[r.header.kid];if(!h)return g.jwks[r.header.kid]=null,{};await m.verify(n,h,d.RS256)}else await m.verify(n,y,d.HS256);const s=r.header.alg===d.RS256?ze(r.header.kid,r.payload,e):r.payload.idpId,i=e[s]||{};if(r.header.alg===d.RS256&&typeof r.payload.exp!="number")return O.warn("Rejected id_token: it has no exp claim"),{};if(r.header.alg===d.RS256&&!Le(r.payload,i))return O.warn("Rejected id_token: it was issued for another project"),{};const c=Xe(i),u=Qe(i);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:s,teams:Array.from(new Set([...X(r.payload.teams||[],u),..."defaultTeams"in i&&i.defaultTeams||[],...X("teamsClaimName"in i&&r.payload[c||""]||[],u),he])),name:Ge(r.payload),isAuthenticated:!0,idpAccessToken:ue(r.payload.idp_access_token,y)||t[I.IDP_ACCESS_TOKEN],federatedAccessToken:t[I.FEDERATED_ACCESS_TOKEN],federatedIdToken:t[I.FEDERATED_ID_TOKEN],authCookie:n}}catch(r){r instanceof we||O.error("Malformed JWT token: %s",r.message)}return{}}function Ge(e){return(e.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||e.email}function Qe(e){switch(e.type){case f.SAML2:return e.teamsAttributeMap;case f.OIDC:return e.teamsClaimMap;default:return}}function Xe(e){switch(e.type){case f.SAML2:return e.teamsAttributeName??U;case f.OIDC:return e.teamsClaimName;default:return U}}function l(e,t){return ee.select(t,e)||[]}export{jt as buildLoginUrl,Oe as buildOidcLoginUrl,Lt as buildOidcLogoutUrl,ve as buildSAML2LoginUrl,Ct as createMcpAuthorizationCode,bt as createMcpRefreshToken,vt as createMcpSessionResource,Ut as decodeSamlResponse,Ke as discoverJwks,je as encodeSAML2,Kt as extractUserClaims,Rt as getAuthProviderLoginParams,ke as getOidcLoginParams,G as getOidcMetadata,$t as getRedoclyTokenPayload,Re as getSaml2LoginParams,zt as getUserParamsFromCookies,Ge as getUsernameFromPayload,Me as idTokenAudienceIsAllowed,Le as idTokenIsForThisProject,Nt as isIdpIssuedToken,x as isOidcProviderConfig,We as isRedoclySso,Ee as isSaml2ProviderConfig,Ot as oidcExchangeCodeForToken,g as oidcJwksCache,N as oidcMetadataCache,Mt as oidcRefreshTokenGrant,Ye as orderTeamsRbacFirst,Vt as parseOidcState,Ce as parsePreviewBranch,Bt as parseSamlResponse,J as rewritePreviewAuthRedirectUri,Ht as signTokenWithinCookieSizeLimit,Dt as verifyMcpAuthorizationCode,Pt as verifyMcpRefreshToken,Ft as verifySAMLResponse};
|
|
@@ -2,6 +2,7 @@ export declare const InternalTokenTypes: {
|
|
|
2
2
|
readonly MCP_CONTEXT: 'mcp_context';
|
|
3
3
|
readonly MCP_AUTHORIZATION_CODE: 'mcp_auth_code';
|
|
4
4
|
readonly MCP_REFRESH_TOKEN: 'mcp_refresh_token';
|
|
5
|
+
readonly MCP_CONSENT: 'mcp_consent';
|
|
5
6
|
readonly AI_ASSISTANT_ANONYMOUS: 'ai_assistant_anonymous';
|
|
6
7
|
};
|
|
7
8
|
export declare const McpAccessTokenMarker: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const _={MCP_CONTEXT:"mcp_context",MCP_AUTHORIZATION_CODE:"mcp_auth_code",MCP_REFRESH_TOKEN:"mcp_refresh_token",MCP_CONSENT:"mcp_consent",AI_ASSISTANT_ANONYMOUS:"ai_assistant_anonymous"},e={CLAIM:"token_use",VALUE:"mcp_access_token"};export{_ as InternalTokenTypes,e as McpAccessTokenMarker};
|