Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions docs/open-collection-gap-analysis/AGENT_PROGRESS.md

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions examples/demo-api/AuthTransport/api-key-query.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
info:
name: API Key Query Auth
type: http
seq: 1
description: Run `node examples/demo-api/server.js`; this request proves API-key query placement reaches the server query string.
http:
method: GET
url: "{{baseUrl}}/auth/api-key-query"
runtime:
auth:
type: apikey
key: demo_key
value: "{{demoToken}}"
placement: query
settings:
encodeUrl: true
timeout: 5000
followRedirects: true
maxRedirects: 5
8 changes: 8 additions & 0 deletions examples/demo-api/AuthTransport/folder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
info:
name: Auth Transport
type: folder
description: Run `node examples/demo-api/server.js` before sending these auth, redirect, OAuth2, proxy, and mTLS demo requests.
request:
headers:
- name: X-Demo-Folder
value: auth-transport
13 changes: 13 additions & 0 deletions examples/demo-api/AuthTransport/mtls-secure.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
info:
name: mTLS Secure
type: http
seq: 4
description: Run `node examples/demo-api/mtls-server.js` and set `missio.rejectUnauthorized` to false for this self-signed local fixture.
http:
method: GET
url: "{{mtlsBaseUrl}}/secure"
settings:
encodeUrl: true
timeout: 5000
followRedirects: true
maxRedirects: 5
33 changes: 33 additions & 0 deletions examples/demo-api/AuthTransport/oauth-query-token.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
info:
name: OAuth2 Query Token
type: http
seq: 2
description: Run `node examples/demo-api/server.js`; this request fetches a local OAuth2 token and sends it as `access_token` query auth.
http:
method: GET
url: "{{baseUrl}}/oauth/resource"
runtime:
auth:
type: oauth2
flow: client_credentials
accessTokenUrl: "{{baseUrl}}/oauth/token?trace=oauth-demo"
credentials:
clientId: demo-client
clientSecret: demo-secret
placement: body
additionalParameters:
accessTokenRequest:
- name: X-Demo-Tenant
value: demo-tenant
placement: header
- name: audience
value: missio-demo
placement: body
tokenConfig:
placement:
query: access_token
settings:
encodeUrl: true
timeout: 5000
followRedirects: true
maxRedirects: 5
13 changes: 13 additions & 0 deletions examples/demo-api/AuthTransport/proxy-through-local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
info:
name: Proxy Through Local
type: http
seq: 5
description: Run `node examples/demo-api/proxy-server.js`, temporarily set `config.proxy.enabled` to true in `opencollection.yml`, then send this request.
http:
method: GET
url: http://upstream.example.test/proxy/demo
settings:
encodeUrl: true
timeout: 5000
followRedirects: true
maxRedirects: 5
13 changes: 13 additions & 0 deletions examples/demo-api/AuthTransport/redirect-follow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
info:
name: Redirect Follow
type: http
seq: 3
description: Run `node examples/demo-api/server.js`; this request follows `/redirect/start` to `/redirect/final`.
http:
method: GET
url: "{{baseUrl}}/redirect/start"
settings:
encodeUrl: true
timeout: 5000
followRedirects: true
maxRedirects: 2
18 changes: 18 additions & 0 deletions examples/demo-api/fixtures/mtls-client.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIICzTCCAbWgAwIBAgIIDUFhaZcNjyIwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UE
AxMJbG9jYWxob3N0MB4XDTI2MDYxMzEyMDcyMVoXDTM2MDYxNDEyMDcyMVowFDES
MBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA3hKGbDq1amxdUszvajxU4rKotgpsKnnMOrqiATDFKeVXcZjlqfv8y0uZBVWG
gjgL9b+DQo1FMS7HrhgO78eZxATGW/DrAxpPPR1tC1E0tPOs5ATZntJ+XO/JSrbK
edNjYc+1pKAFn4hyTjuLR5+sGRleg4R4RUNPxTEFraY4FQEd99SadbTSx6li6DTC
kbgBTxE3F/JdqDViaPjO2s8TqutrCRYU4CHBrrNi0pZtTTm7v1SZO2HLHmqKM5AN
g4wSXT6yZpoC6isoupay087cKHd/AdTf1nDGYvlKRLj3gMZ5DZQz4VegvBP4qU6l
d79pd+ipYr7mAtEZuyY31vhNDQIDAQABoyMwITAPBgNVHRMBAf8EBTADAQH/MA4G
A1UdDwEB/wQEAwICpDANBgkqhkiG9w0BAQsFAAOCAQEAo/hFkKGTaPqqN9JjH/dA
Zpp5RrMiF2m7IKh8WTFOncbwoImToxKA4KFKJlThLqPw4QSujrggxzc+QM+Tazxl
125tKFKH1ZY0po7KCbYkBEL404JccdD+dDmvU8UEmF+GDMSo1HB2E9y/n46kYRSt
wzR9ksZG2AiWSwgkfzWcO+AKXySY3jpjyHsjD8HEB89XHfAedxOUiZp63DRs4Iu4
R6TwUvQsVJg9JL/U/znMKxqomqVPZ9u2sVM6u+eceaO3A+Tt6A0QxyIwS1QtJ7o5
GmcSuy7+TSf5CvEsn5P8G3J47L6EOzvx16qT7VAEW4oenwNxyg8wvL4B033fYYzT
Bw==
-----END CERTIFICATE-----
27 changes: 27 additions & 0 deletions examples/demo-api/fixtures/mtls-client.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA3hKGbDq1amxdUszvajxU4rKotgpsKnnMOrqiATDFKeVXcZjl
qfv8y0uZBVWGgjgL9b+DQo1FMS7HrhgO78eZxATGW/DrAxpPPR1tC1E0tPOs5ATZ
ntJ+XO/JSrbKedNjYc+1pKAFn4hyTjuLR5+sGRleg4R4RUNPxTEFraY4FQEd99Sa
dbTSx6li6DTCkbgBTxE3F/JdqDViaPjO2s8TqutrCRYU4CHBrrNi0pZtTTm7v1SZ
O2HLHmqKM5ANg4wSXT6yZpoC6isoupay087cKHd/AdTf1nDGYvlKRLj3gMZ5DZQz
4VegvBP4qU6ld79pd+ipYr7mAtEZuyY31vhNDQIDAQABAoIBAAk1v3lxneCCCgTL
FwrS4bpdKn4SRJYmYv/0iY9/FE4+grflXXEFUGCmC/yapW91H5nbjXgPH9WAWSux
N71eC9SDVi6t+TExwCOKuuEDRypSCNOUF+psVG1KTJDar98Jk0+VK7VeJZ2OLR9t
fMNFrf+Ee9T8g3hr6D0HYXLoN983Dt1sY/rTFfLBt4uVdGfuYbF+W/2id9od+mfn
kDWQID54mNCjWVyiBwIVQr1S2zDzliq6m9gRmjFhLWzhSYTVu+R9xywJesnlFwnI
KjsVP6Jv1kJCINrSZJk4HLfRz/uKuc1IaoCcY0a8vufX2ldW1dbknHc6JN5b6Dj+
+mWwM8kCgYEA6nm0LQwiYJN0ao7pFngT/xoWTXZn+/dxFK4S15wiPL0MLf66I+KD
E9IocUrYClWRPBhWzTjg7xWcejWDvCI7UQOUozwusO/54iEgVbbzsad95i8tFzst
4i89voK7Iz67o/mBmHLKOTfoTJY6C2SemewNDVjBcj3s8/GT2cBW308CgYEA8nVX
3ZGYZrpcT9Hy8vyOkcJxov3M7QgwMUvy8++3B6lpwqtnPmn+B7SVCcaCn6RS3yDL
0upxk5s7Dhps6oQ8J215fZH2o5oiHjSRrT4PFvFLqZREZ+GYd7w8loBjMWAdiwvz
u+x9l3IVqoMXaPw5YG5t2RZdg4uNcN0MreVoluMCgYEAg/9rnQh9udyI5wv4z/td
Vnk7IPSNaV1NPZUZamOtKoBKgQIri9QScnAW8GBv6rFtB2W0R+fDSRTjeDD0Lk8f
EWZwoMxahKU0CUcYyugpnFNsHs9kFPXtyK1LlxpFe3vvakol2MqWaUu97I+Nsag9
WO14E5FppYSTBmlzEFylCyUCgYAPGgP5BwKJE36AckFBpT10ErplPo2vDd2ClIpz
azDpR0IRH//0QUHTVQoba8PjEacfwrkvT+73FKoe/MJf8RCWHBl/GsJT+lu5qeiQ
89aYxTrDOzrvhXurqYvUi/ahsqzkZkAuKlLARhjXYAbrQRqJyRcKeHwmn2CV8Q7D
HhDfpQKBgQDe7lorOWe0HvHNoCzgHN2DWNKAsOPcQm5W8q6CGzPETv1GxlDFxpA7
mMXqBgl4GsZ2R9UDaQ29yGFV9sCg2nTp1AR7nqp4550dSi+pcCEgG0+IMmpDcnp0
y3drEao15oS8HyXU98l8FoYM2pE+6G/JK8YW2fbGGNdYPRuHhlU+0A==
-----END RSA PRIVATE KEY-----
48 changes: 48 additions & 0 deletions examples/demo-api/mtls-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';

const https = require('https');
const fs = require('fs');
const path = require('path');

const PORT = 3443;
const CERT_PATH = path.join(__dirname, 'fixtures', 'mtls-client.crt');
const KEY_PATH = path.join(__dirname, 'fixtures', 'mtls-client.key');

const cert = fs.readFileSync(CERT_PATH);
const key = fs.readFileSync(KEY_PATH);

function json(res, status, data) {
const body = JSON.stringify(data, null, 2);
res.writeHead(status, {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body),
});
res.end(body);
}

const server = https.createServer(
{
key,
cert,
ca: cert,
requestCert: true,
rejectUnauthorized: true,
},
(req, res) => {
if (req.method === 'GET' && req.url === '/secure') {
const peer = req.socket.getPeerCertificate();
return json(res, req.client.authorized ? 200 : 401, {
ok: req.client.authorized,
route: '/secure',
clientCommonName: peer && peer.subject ? peer.subject.CN : null,
});
}

return json(res, 404, { error: 'Not Found', path: req.url });
},
);

server.listen(PORT, '127.0.0.1', () => {
console.log(`Missio mTLS Demo Server -> https://127.0.0.1:${PORT}`);
console.log('Set missio.rejectUnauthorized=false for this self-signed local fixture.');
});
17 changes: 17 additions & 0 deletions examples/demo-api/opencollection.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,28 @@ config:
path: proto/services/missio_demo.proto
importPaths:
- path: proto
proxy:
enabled: false
config:
protocol: http
hostname: 127.0.0.1
port: 3457
auth:
username: demo-proxy
password: demo-proxy
bypassProxy: localhost,127.0.0.1
clientCertificates:
- domain: 127.0.0.1
type: pem
certificateFilePath: fixtures/mtls-client.crt
privateKeyFilePath: fixtures/mtls-client.key
environments:
- name: LOCAL
variables:
- name: baseUrl
value: http://localhost:3456
- name: mtlsBaseUrl
value: https://127.0.0.1:3443
- name: grpcBaseUrl
value: localhost:50051
- name: userId
Expand Down
39 changes: 39 additions & 0 deletions examples/demo-api/proxy-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';

const http = require('http');

const PORT = 3457;
const REQUIRED_AUTH = 'Basic ' + Buffer.from('demo-proxy:demo-proxy').toString('base64');

function json(res, status, data) {
const body = JSON.stringify(data, null, 2);
res.writeHead(status, {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body),
});
res.end(body);
}

const server = http.createServer((req, res) => {
if (req.headers['proxy-authorization'] !== REQUIRED_AUTH) {
res.writeHead(407, {
'Proxy-Authenticate': 'Basic realm="Missio Demo Proxy"',
'Content-Length': '0',
});
res.end();
return;
}

json(res, 200, {
ok: true,
route: 'local-proxy',
method: req.method,
targetUrl: req.url,
proxyAuthorized: true,
});
});

server.listen(PORT, '127.0.0.1', () => {
console.log(`Missio Proxy Demo Server -> http://127.0.0.1:${PORT}`);
console.log('Temporarily set config.proxy.enabled=true in examples/demo-api/opencollection.yml to use it.');
});
56 changes: 56 additions & 0 deletions examples/demo-api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ const server = http.createServer(async (req, res) => {

const { method, url, headers } = req;
const contentType = headers['content-type'] || 'application/octet-stream';
const requestUrl = new URL(url, `http://localhost:${PORT}`);

// Pre-flight
if (method === 'OPTIONS') {
Expand Down Expand Up @@ -184,6 +185,57 @@ const server = http.createServer(async (req, res) => {
return json(res, 200, buildGraphQLFixture(payload, headers));
}

if (method === 'GET' && requestUrl.pathname === '/auth/api-key-query') {
const key = requestUrl.searchParams.get('demo_key');
return json(res, key === 'demo-token' ? 200 : 401, {
ok: key === 'demo-token',
route: '/auth/api-key-query',
receivedKey: key,
placement: 'query',
});
}

if (method === 'POST' && requestUrl.pathname === '/oauth/token') {
const body = await collectBody(req);
const params = new URLSearchParams(body.toString('utf8'));
return json(res, 200, {
access_token: 'demo-oauth-token',
token_type: 'Bearer',
expires_in: 3600,
echoed: {
queryTrace: requestUrl.searchParams.get('trace'),
tenant: headers['x-demo-tenant'] || null,
audience: params.get('audience'),
grantType: params.get('grant_type'),
},
});
}

if (method === 'GET' && requestUrl.pathname === '/oauth/resource') {
const queryToken = requestUrl.searchParams.get('access_token');
const headerToken = (headers.authorization || '').replace(/^Bearer\s+/i, '');
const token = queryToken || headerToken;
return json(res, token === 'demo-oauth-token' ? 200 : 401, {
ok: token === 'demo-oauth-token',
route: '/oauth/resource',
tokenPlacement: queryToken ? 'query' : headerToken ? 'header' : 'missing',
});
}

if (method === 'GET' && requestUrl.pathname === '/redirect/start') {
res.writeHead(302, { Location: '/redirect/final' });
res.end();
return;
}

if (method === 'GET' && requestUrl.pathname === '/redirect/final') {
return json(res, 200, {
ok: true,
route: '/redirect/final',
followed: true,
});
}

// Generic binary upload: accepts anything, returns metadata.
if (method === 'POST' && url === '/upload') {
const body = await collectBody(req);
Expand Down Expand Up @@ -404,6 +456,10 @@ server.listen(PORT, '127.0.0.1', () => {
console.log(` GET http://localhost:${PORT}/health`);
console.log(` GET http://localhost:${PORT}/graphql (GraphQL fixture info)`);
console.log(` POST http://localhost:${PORT}/graphql (GraphQL JSON body -> JSON response)`);
console.log(` GET http://localhost:${PORT}/auth/api-key-query?demo_key=demo-token`);
console.log(` POST http://localhost:${PORT}/oauth/token (OAuth2 token fixture)`);
console.log(` GET http://localhost:${PORT}/oauth/resource (OAuth2 protected resource)`);
console.log(` GET http://localhost:${PORT}/redirect/start (302 to /redirect/final)`);
console.log(` POST http://localhost:${PORT}/upload (any binary body → JSON info)`);
console.log(` POST http://localhost:${PORT}/upload/image (image body → echoed back)`);
console.log(` POST http://localhost:${PORT}/upload/pdf (PDF body → JSON info)`);
Expand Down
Loading
Loading