Skip to content

Commit

Permalink
fix: re-enable support for multiple providers (#217)
Browse files Browse the repository at this point in the history
we lost this in the D1 migration work - go back to parsing the PROVIDERS
env var and use the result as the list of supported providers
  • Loading branch information
travis authored Aug 14, 2023
1 parent 3b032d1 commit 1d7370f
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 13 deletions.
1 change: 1 addition & 0 deletions stacks/upload-api-stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export function UploadApiStack({ stack, app }) {
ACCESS_SERVICE_DID: process.env.ACCESS_SERVICE_DID ?? '',
ACCESS_SERVICE_URL: process.env.ACCESS_SERVICE_URL ?? '',
POSTMARK_TOKEN: process.env.POSTMARK_TOKEN ?? '',
PROVIDERS: process.env.PROVIDERS ?? '',
R2_ACCESS_KEY_ID: process.env.R2_ACCESS_KEY_ID ?? '',
R2_SECRET_ACCESS_KEY: process.env.R2_SECRET_ACCESS_KEY ?? '',
R2_REGION: process.env.R2_REGION ?? '',
Expand Down
20 changes: 19 additions & 1 deletion upload-api/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,29 @@ import * as DID from '@ipld/dag-ucan/did'
* @param {string} [config.UPLOAD_API_DID] - public DID for the upload service (did:key:... derived from PRIVATE_KEY if not set)
* @returns {import('@ucanto/principal/ed25519').Signer.Signer}
*/
export function getServiceSigner(config) {
export function getServiceSigner(config) {
const signer = ed25519.parse(config.PRIVATE_KEY)
if (config.UPLOAD_API_DID) {
const did = DID.parse(config.UPLOAD_API_DID).did()
return signer.withDID(did)
}
return signer
}

/**
* Given a string, parse into provider ServiceDIDs.
*
* @param {string} serviceDids a comma-separated string of ServiceDIDs
* @returns {import('@web3-storage/upload-api').ServiceDID[]}
*/
export function parseServiceDids(serviceDids) {
return /** @type {import('@web3-storage/upload-api').ServiceDID[]} */(
serviceDids.split(',').map(s => {
const did = DID.parse(s.trim()).did()
if (!did.startsWith('did:web:')) {
throw new Error(`Invalid ServiceDID - ServiceDID must be a did:web: ${did}`)
}
return did
})
)
}
9 changes: 3 additions & 6 deletions upload-api/functions/ucan-invocation-router.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { createTaskStore } from '../buckets/task-store.js'
import { createWorkflowStore } from '../buckets/workflow-store.js'
import { createStoreTable } from '../tables/store.js'
import { createUploadTable } from '../tables/upload.js'
import { getServiceSigner } from '../config.js'
import { getServiceSigner, parseServiceDids } from '../config.js'
import { createUcantoServer } from '../service.js'
import { Config } from '@serverless-stack/node/config/index.js'
import { CAR, Legacy, Codec } from '@ucanto/transport'
Expand Down Expand Up @@ -95,9 +95,9 @@ export async function ucanInvocationRouter(request) {
WORKFLOW_BUCKET_NAME: workflowBucketName = '',
UCAN_LOG_STREAM_NAME: streamName = '',
POSTMARK_TOKEN: postmarkToken = '',
PROVIDERS: providers = '',
// set for testing
DYNAMO_DB_ENDPOINT: dbEndpoint,
ACCESS_SERVICE_DID: accessServiceDID = '',
ACCESS_SERVICE_URL: accessServiceURL = '',
} = process.env

Expand Down Expand Up @@ -125,10 +125,7 @@ export async function ucanInvocationRouter(request) {
endpoint: dbEndpoint
});
const rateLimitsStorage = createRateLimitTable(AWS_REGION, rateLimitTableName)
const provisionsStorage = useProvisionStore(subscriptionTable, consumerTable, [
/** @type {import('@web3-storage/upload-api').ServiceDID} */
(accessServiceDID)
])
const provisionsStorage = useProvisionStore(subscriptionTable, consumerTable, parseServiceDids(providers))
const delegationsStorage = createDelegationsTable(AWS_REGION, delegationTableName, { bucket: delegationBucket, invocationBucket, workflowBucket })

const server = createUcantoServer(serviceSigner, {
Expand Down
9 changes: 3 additions & 6 deletions upload-api/functions/validate-email.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Sentry from '@sentry/serverless'
import { authorize } from '@web3-storage/upload-api/validate'
import { Config } from '@serverless-stack/node/config/index.js'
import { getServiceSigner } from '../config.js'
import { getServiceSigner, parseServiceDids } from '../config.js'
import { Email } from '../email.js'
import { createDelegationsTable } from '../tables/delegations.js'
import { createDelegationsStore } from '../buckets/delegations-store.js'
Expand Down Expand Up @@ -64,7 +64,6 @@ export const preValidateEmail = Sentry.AWSLambda.wrapHandler(validateEmailGet)
function createAuthorizeContext () {
const {
ACCESS_SERVICE_URL = '',
ACCESS_SERVICE_DID = '',
AWS_REGION = '',
DELEGATION_TABLE_NAME = '',
RATE_LIMIT_TABLE_NAME = '',
Expand All @@ -78,6 +77,7 @@ function createAuthorizeContext () {
SUBSCRIPTION_TABLE_NAME = '',
CONSUMER_TABLE_NAME = '',
UPLOAD_API_DID = '',
PROVIDERS = '',
// set for testing
DYNAMO_DB_ENDPOINT: dbEndpoint,
} = process.env
Expand All @@ -100,10 +100,7 @@ function createAuthorizeContext () {
email: new Email({ token: POSTMARK_TOKEN }),
signer: getServiceSigner({ UPLOAD_API_DID, PRIVATE_KEY }),
delegationsStorage: createDelegationsTable(AWS_REGION, DELEGATION_TABLE_NAME, { bucket: delegationBucket, invocationBucket, workflowBucket }),
provisionsStorage: useProvisionStore(subscriptionTable, consumerTable, [
/** @type {import('@web3-storage/upload-api').ServiceDID} */
(ACCESS_SERVICE_DID)
]),
provisionsStorage: useProvisionStore(subscriptionTable, consumerTable, parseServiceDids(PROVIDERS)),
rateLimitsStorage: createRateLimitTable(AWS_REGION, RATE_LIMIT_TABLE_NAME)
}
}
Expand Down
45 changes: 45 additions & 0 deletions upload-api/test/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,48 @@ test('getServiceSigner errors if config.UPLOAD_API_DID is provided but not a DID
})
}, { message: /^Invalid DID/ })
})

test('parseServiceDids parses one DID', async (t) => {
t.deepEqual(
configModule.parseServiceDids('did:web:example.com'),
['did:web:example.com']
)
})

test('parseServiceDids parses more than one DID', async (t) => {
t.deepEqual(
configModule.parseServiceDids('did:web:example.com,did:web:two.example.com'),
['did:web:example.com', 'did:web:two.example.com']
)

t.deepEqual(
configModule.parseServiceDids('did:web:example.com,did:web:two.example.com,did:web:three.example.com'),
['did:web:example.com', 'did:web:two.example.com', 'did:web:three.example.com']
)
})

test('parseServiceDids trims space around dids', async (t) => {
t.deepEqual(
configModule.parseServiceDids(' did:web:example.com, did:web:two.example.com '),
['did:web:example.com', 'did:web:two.example.com']
)
})

test('parseServiceDids throws an exception if a non-DID is provided', async (t) => {
t.throws(
() => configModule.parseServiceDids('http://example.com'),
{ message: /^Invalid DID/}
)
})

test('parseServiceDids throws an exception if a non-ServiceDID is provided', async (t) => {
t.throws(
() => configModule.parseServiceDids('did:mailto:abc123'),
{ message: /^Invalid ServiceDID/}
)

t.throws(
() => configModule.parseServiceDids('did:key:z6Mkfy8k2JJUdNWCJtvzYrko5QRc7GXP6pksKDG19gxYzyi4'),
{ message: /^Invalid ServiceDID/}
)
})

0 comments on commit 1d7370f

Please sign in to comment.