diff --git a/pocs/README.md b/pocs/README.md new file mode 100644 index 0000000..7f7f617 --- /dev/null +++ b/pocs/README.md @@ -0,0 +1,34 @@ +# Developing PoCs. + +## Sharing + +When developing a GraphQL PoC using IBM API Connect Essentials (StepZen) you will typically +want to share the endpoint to allow others to evaluate it by making GraphQL requests against it. + +### Admin key + +> [!CAUTION] +> Never share an endpoint by handing out the account's adminkey (`stepzen whoami --adminkey`). + +### API key + +The most simple (but somewhat risky) approach is to share the account's apikey (`stepzen whoami --apikey`). + +> [!WARNING] +> The admin key grants access to all endpoints within an account. Thus if you are working on different PoCs, by providing access to one you provide access to all +> which could leak information between different departments or clients. + +### Obfuscated endpoints + +Obfuscated endpoints allow sharing without handing out any keys or tokens. + +The concept can include single-use or short-lived endpoints for a demos, or time-limited evaluations. + +> [!WARNING] +> Obfuscated endpoints are public, but have a hard to guess endpoint URL so anyone with the URL has access to the API. + +See [obfuscated-endpoint-url](obfuscated-endpoint-url/README.md) + +### JWT + +_Coming Soon_! diff --git a/pocs/obfuscated-endpoint-url/README.md b/pocs/obfuscated-endpoint-url/README.md new file mode 100644 index 0000000..66e1ee0 --- /dev/null +++ b/pocs/obfuscated-endpoint-url/README.md @@ -0,0 +1,69 @@ +# Obfuscated endpoint URL + +One simple mechanism to share an IBM API Connect Essentials (StepZen) endpoint to +to make the [endpoint open](../../protection/makeAllPublic/config.yaml) but +deploy the schema with an obfuscated name. + +## Deploying + +For example, deploy this schema using a randomly generated name (Linux/MacOS): + +``` +name=$(cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z' | fold -w 64 | head -n 1) +stepzen deploy pocs/$name +``` + +You will see the endpoint is deployed with a random obfuscated URL. + +``` +Deploying pocs/FzOYquoOMuQzvQqsLSUfVuvQwfVwuOEhGOkGGpLDnuIzeJZCHQAfHbFMCCIQdmBe to StepZen... done in 511ms 🚀 + ✓ 🔐 https://danville.us-east-a.ibm.stepzen.net/pocs/FzOYquoOMuQzvQqsLSUfVuvQwfVwuOEhGOkGGpLDnuIzeJZCHQAfHbFMCCIQdmBe/__graphql + ✓ 🔐 wss://danville.us-east-a.ibm.stepzen.net/stepzen-subscriptions/pocs/FzOYquoOMuQzvQqsLSUfVuvQwfVwuOEhGOkGGpLDnuIzeJZCHQAfHbFMCCIQdmBe/__graphql (subscriptions) +``` + +This endpoint URL can now be handed out to allow others to evaluate the endpoint without requiring any authorization. + +> [!WARNING] +> Anyone with the URL has access to the endpoint, so this is security through obscurity. + +## Analytics + +Using the analytics dashboard the account owner can see activity with this specific endpoint, +so by handing out individual endpoints the account owner can track how extensively the endpoint has been evaluated. + +## Single-use & deleting + +Thus one can extend thise concept to a "single-use" endpoint, for example creating an endpoint +for a demo and then delete it when no longer required. + +``` +stepzen delete --non-interactive pocs/FzOYquoOMuQzvQqsLSUfVuvQwfVwuOEhGOkGGpLDnuIzeJZCHQAfHbFMCCIQdmBe +``` + +## Tracking + +By maintaining such endpoints in single folder, such as `pocs` or `single-use` you can use `stepzen list` to +see which endpoints are still active. + +``` +> stepzen list --folder pocs + Endpoint Created at Updated at + ──────────────────────────────────────────────────────────────────────── ──────────────────────── ───────────────────── + pocs/FzOYquoOMuQzvQqsLSUfVuvQwfVwuOEhGOkGGpLDnuIzeJZCHQAfHbFMCCIQdmBe Sep 8, 2024, 12:46 PM Sep 8, 2024, 12:59 PM +``` + +## Schema description + +It is recommended to define a [schema description](./index.graphql#L1-L6) so that this GraphQL introspection request can +used to see the purpose of the obfuscated endpoint. + +``` +> stepzen request '{__schema{description}}' +{ + "data": { + "__schema": { + "description": "Sample mocked Customer endpoint." + } + } +} +``` diff --git a/pocs/obfuscated-endpoint-url/config.yaml b/pocs/obfuscated-endpoint-url/config.yaml new file mode 100644 index 0000000..86c88bf --- /dev/null +++ b/pocs/obfuscated-endpoint-url/config.yaml @@ -0,0 +1,5 @@ +access: + policies: + - type: Query + policyDefault: + condition: true # allow all fields in Query with no authorization diff --git a/pocs/obfuscated-endpoint-url/index.graphql b/pocs/obfuscated-endpoint-url/index.graphql new file mode 100644 index 0000000..13d6b71 --- /dev/null +++ b/pocs/obfuscated-endpoint-url/index.graphql @@ -0,0 +1,22 @@ +""" +Sample mocked Customer endpoint. +""" +schema { + query: Query +} + +type Customer @mock { + id: ID! + name: String @mockfn(name: "LastName") + email: String @mockfn(name: "Email") + address: Address +} + +type Address { + city: String @mockfn(name: "City") + zip: String @mockfn(name: "Zip") +} + +type Query { + customer(id: ID): Customer +} diff --git a/pocs/obfuscated-endpoint-url/operations.graphql b/pocs/obfuscated-endpoint-url/operations.graphql new file mode 100644 index 0000000..d5d136e --- /dev/null +++ b/pocs/obfuscated-endpoint-url/operations.graphql @@ -0,0 +1,11 @@ +query Customer($id: ID!) { + customer(id: $id) { + id + name + email + address { + city + zip + } + } +} diff --git a/pocs/obfuscated-endpoint-url/stepzen.config.json b/pocs/obfuscated-endpoint-url/stepzen.config.json new file mode 100644 index 0000000..63283a6 --- /dev/null +++ b/pocs/obfuscated-endpoint-url/stepzen.config.json @@ -0,0 +1,3 @@ +{ + "endpoint": "api/misc" +} \ No newline at end of file diff --git a/pocs/obfuscated-endpoint-url/tests/Test.js b/pocs/obfuscated-endpoint-url/tests/Test.js new file mode 100644 index 0000000..af88a99 --- /dev/null +++ b/pocs/obfuscated-endpoint-url/tests/Test.js @@ -0,0 +1,37 @@ +const fs = require("fs"); +const path = require("node:path"); + +const { + deployAndRun, + getTestDescription, +} = require("../../../tests/gqltest.js"); + +testDescription = getTestDescription("snippets", __dirname); + +const requestsFile = path.join(path.dirname(__dirname), "operations.graphql"); +const requests = fs.readFileSync(requestsFile, "utf8").toString(); + +describe(testDescription, function () { + const tests = [ + { + label: "customer", + query: requests, + operationName: "Customer", + variables: { + id: 1238, + }, + expected: { + customer: { + id: "1238", + name: "Baumbach", + email: "maxinebashirian@hilpert.com", + address: { + city: "New Schroeder", + zip: "36897", + }, + }, + }, + }, + ]; + return deployAndRun(__dirname, tests); +});