Skip to content

Commit

Permalink
Revoke presign URLs (#65)
Browse files Browse the repository at this point in the history
* support to revoke presign URLs
  • Loading branch information
sandeep-paliwal authored Nov 10, 2020
1 parent 1c0f6b6 commit 57aca28
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 6 deletions.
27 changes: 23 additions & 4 deletions lib/TvmClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ function createCacheKey (url, namespace, auth) {
* @property {string} signature sas signature for the blob
*/

/**
* Tvm response for Azure Presign revoke.
*
* @typedef TvmResponseAzureRevoke
* @type {object} empty response
*/

/**
* Tvm response with Azure Cosmos resource credentials. Gives access to an isolated partition within a CosmosDB container.
*
Expand Down Expand Up @@ -210,7 +217,7 @@ class TvmClient {
return new TvmClient(config)
}

async _getCredentialsFromTVM (url, params = {}) {
async _requestTVM (url, params = {}) {
const fullUrl = new URL(urlJoin(url, this.ow.namespace))
// add query params
Object.keys(params).forEach(k => fullUrl.searchParams.append(k, params[k]))
Expand All @@ -223,7 +230,7 @@ class TvmClient {
}
}, this.retryOptions)
if (response.ok) {
logger.debug(`successfully fetched credentials from tvm for ${fullUrl}`)
logger.debug(`successfully made TVM request for ${fullUrl}`)
return response.json()
}
const errorBody = await response.text()
Expand Down Expand Up @@ -316,7 +323,7 @@ class TvmClient {
creds = await this._getCredentialsFromCacheFile(cacheKey)
}
if (!creds) {
creds = await this._getCredentialsFromTVM(fullUrl)
creds = await this._requestTVM(fullUrl)
this._cacheCredentials(cacheKey, creds)
await this._cacheCredentialsToFile(cacheKey, creds)
}
Expand Down Expand Up @@ -405,18 +412,30 @@ class TvmClient {
}))
}
// no caching here
return this._getCredentialsFromTVM(
return this._requestTVM(
urlJoin(this.apiUrl, TvmClient.AzurePresignEndpoint),
{
blobName: options.blobName,
expiryInSeconds: options.expiryInSeconds,
permissions: options.permissions
})
}

/**
* Revoke all presigned URLs for Azure blob storage.
*
* @returns {Promise<TvmResponseAzureRevoke>} success response
* @throws {codes.ERROR_RESPONSE}
*/
async revokePresignURLs () {
return this._requestTVM(
urlJoin(this.apiUrl, TvmClient.AzureRevokePresignEndpoint))
}
}

TvmClient.AzureBlobEndpoint = 'azure/blob'
TvmClient.AzurePresignEndpoint = 'azure/presign'
TvmClient.AzureRevokePresignEndpoint = 'azure/revoke'
TvmClient.AwsS3Endpoint = 'aws/s3'
TvmClient.AzureCosmosEndpoint = 'azure/cosmos'
TvmClient.inMemoryCache = null
Expand Down
41 changes: 39 additions & 2 deletions test/TvmClient.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,47 @@ describe('getAzurePresignCredentials', () => {
expect(mockLogError).toHaveBeenCalledWith(expect.stringContaining('ERROR_MISSING_OPTION'))
})
})
describe('revokePresignURLs', () => {
const fetchTvmPresignLog = 'successfully made TVM request for'

test('when tvm response is valid', async () => {
// fake the fetch to the TVM
fetch.mockResolvedValue(wrapInFetchResponse(fakeAzureTVMPresignResponse))
fakeTVMInput.cacheFile = false
const tvmClient = await TvmClient.init(fakeTVMInput)
const creds = await tvmClient.revokePresignURLs()
expect(creds).toEqual(fakeAzureTVMPresignResponse)
// calls with namespace as path arg
expect(fetch.mock.calls[0][0].toString()).toEqual(TvmClient.DefaultApiHost + '/' +
TvmClient.AzureRevokePresignEndpoint + '/' + fakeTVMInput.ow.namespace)
// adds Authorization header
expect(fetch.mock.calls[0][1].headers).toEqual(expect.objectContaining({ Authorization: 'Basic ZmFrZWF1dGg=', 'x-Api-Key': 'firefly-aio-tvm' }))
expect(mockLogDebug).toHaveBeenCalledWith(expect.stringContaining(fetchTvmPresignLog))
})
test('when tvm response has a client error', async () => {
// fake the fetch to the TVM
fetch.mockResolvedValue(wrapInFetchError(400))
fakeTVMInput.cacheFile = false
const tvmClient = await TvmClient.init(fakeTVMInput)
await expect(tvmClient.revokePresignURLs()).rejects.toThrow('[TvmLib:ERROR_RESPONSE] error response from TVM server with status code: 400')
expect(mockLogError).toHaveBeenCalledWith(expect.stringContaining(fakeTVMInput.ow.namespace))
expect(mockLogError).toHaveBeenCalledWith(expect.not.stringContaining(fakeTVMInput.ow.auth))
})
test('when tvm fetch is unauthorized', async () => {
// fake the fetch to the TVM
fetch.mockResolvedValue(wrapInFetchError(401))
fakeTVMInput.cacheFile = false
const tvmClient = await TvmClient.init(fakeTVMInput)
await expect(tvmClient.revokePresignURLs()).rejects.toThrow('[TvmLib:ERROR_RESPONSE] error response from TVM server with status code: 401')
expect(mockLogError).toHaveBeenCalledWith(expect.stringContaining(fakeTVMInput.ow.namespace))
expect(mockLogError).toHaveBeenCalledWith(expect.not.stringContaining(fakeTVMInput.ow.auth))
})
})

describe('getAzureBlobCredentials', () => {
const readCacheLog = 'read credentials from cache file'
const writeCacheLog = 'wrote credentials to cache file'
const fetchTvmLog = 'fetched credentials from tvm'
const fetchTvmLog = 'successfully made TVM request for'
const expiredCacheLog = 'expired'
describe('without caching cacheFile=false', () => {
test('when tvm response is valid', async () => {
Expand Down Expand Up @@ -450,7 +487,7 @@ describe('getAzureCosmosCredentials', () => {
describe('with in memory caching', () => {
const readCacheLog = 'read credentials from cache with key'
const writeCacheLog = 'wrote credentials to cache with key'
const fetchTvmLog = 'fetched credentials from tvm'
const fetchTvmLog = 'successfully made TVM request for'
const expiredCacheLog = 'expired'
// the general tests are same, we test just that the method is defined
test('with cacheFile set to false', async () => {
Expand Down

0 comments on commit 57aca28

Please sign in to comment.