You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-17 01:12:23 +02:00
Allow redactions for HTTP queries.
The Azure storage driver exposes secrets in the query when using SAS authorization. These secrets can show up during logging or when an error occurs. Allow redaction of queries to prevent secrets from being exposed in logs and errors.
This commit is contained in:
@ -27,7 +27,7 @@ cmdRepoCreate(void)
|
|||||||
{
|
{
|
||||||
storageAzureRequestP(
|
storageAzureRequestP(
|
||||||
(StorageAzure *)storageDriver(storageRepoWrite()), HTTP_VERB_PUT_STR,
|
(StorageAzure *)storageDriver(storageRepoWrite()), HTTP_VERB_PUT_STR,
|
||||||
.query = httpQueryAdd(httpQueryNew(), AZURE_QUERY_RESTYPE_STR, AZURE_QUERY_VALUE_CONTAINER_STR));
|
.query = httpQueryAdd(httpQueryNewP(), AZURE_QUERY_RESTYPE_STR, AZURE_QUERY_VALUE_CONTAINER_STR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MEM_CONTEXT_TEMP_END();
|
MEM_CONTEXT_TEMP_END();
|
||||||
|
@ -17,6 +17,7 @@ struct HttpQuery
|
|||||||
{
|
{
|
||||||
MemContext *memContext; // Mem context
|
MemContext *memContext; // Mem context
|
||||||
KeyValue *kv; // KeyValue store
|
KeyValue *kv; // KeyValue store
|
||||||
|
const StringList *redactList; // List of keys to redact values for
|
||||||
};
|
};
|
||||||
|
|
||||||
OBJECT_DEFINE_MOVE(HTTP_QUERY);
|
OBJECT_DEFINE_MOVE(HTTP_QUERY);
|
||||||
@ -24,9 +25,11 @@ OBJECT_DEFINE_FREE(HTTP_QUERY);
|
|||||||
|
|
||||||
/**********************************************************************************************************************************/
|
/**********************************************************************************************************************************/
|
||||||
HttpQuery *
|
HttpQuery *
|
||||||
httpQueryNew(void)
|
httpQueryNew(HttpQueryNewParam param)
|
||||||
{
|
{
|
||||||
FUNCTION_TEST_VOID();
|
FUNCTION_TEST_BEGIN();
|
||||||
|
FUNCTION_TEST_PARAM(STRING_LIST, param.redactList);
|
||||||
|
FUNCTION_TEST_END();
|
||||||
|
|
||||||
HttpQuery *this = NULL;
|
HttpQuery *this = NULL;
|
||||||
|
|
||||||
@ -39,6 +42,7 @@ httpQueryNew(void)
|
|||||||
{
|
{
|
||||||
.memContext = MEM_CONTEXT_NEW(),
|
.memContext = MEM_CONTEXT_NEW(),
|
||||||
.kv = kvNew(),
|
.kv = kvNew(),
|
||||||
|
.redactList = strLstDup(param.redactList),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
MEM_CONTEXT_NEW_END();
|
MEM_CONTEXT_NEW_END();
|
||||||
@ -101,10 +105,11 @@ httpQueryNewStr(const String *query)
|
|||||||
|
|
||||||
/**********************************************************************************************************************************/
|
/**********************************************************************************************************************************/
|
||||||
HttpQuery *
|
HttpQuery *
|
||||||
httpQueryDup(const HttpQuery *query)
|
httpQueryDup(const HttpQuery *query, HttpQueryDupParam param)
|
||||||
{
|
{
|
||||||
FUNCTION_TEST_BEGIN();
|
FUNCTION_TEST_BEGIN();
|
||||||
FUNCTION_TEST_PARAM(HTTP_QUERY, query);
|
FUNCTION_TEST_PARAM(HTTP_QUERY, query);
|
||||||
|
FUNCTION_TEST_PARAM(STRING_LIST, param.redactList);
|
||||||
FUNCTION_TEST_END();
|
FUNCTION_TEST_END();
|
||||||
|
|
||||||
HttpQuery *this = NULL;
|
HttpQuery *this = NULL;
|
||||||
@ -120,6 +125,7 @@ httpQueryDup(const HttpQuery *query)
|
|||||||
{
|
{
|
||||||
.memContext = MEM_CONTEXT_NEW(),
|
.memContext = MEM_CONTEXT_NEW(),
|
||||||
.kv = kvDup(query->kv),
|
.kv = kvDup(query->kv),
|
||||||
|
.redactList = param.redactList != NULL ? strLstDup(param.redactList) : strLstDup(query->redactList),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
MEM_CONTEXT_NEW_END();
|
MEM_CONTEXT_NEW_END();
|
||||||
@ -231,38 +237,61 @@ httpQueryPut(HttpQuery *this, const String *key, const String *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************************************************************************************/
|
/**********************************************************************************************************************************/
|
||||||
String *
|
bool
|
||||||
httpQueryRender(const HttpQuery *this)
|
httpQueryRedact(const HttpQuery *this, const String *key)
|
||||||
{
|
{
|
||||||
FUNCTION_TEST_BEGIN();
|
FUNCTION_TEST_BEGIN();
|
||||||
FUNCTION_TEST_PARAM(HTTP_QUERY, this);
|
FUNCTION_TEST_PARAM(HTTP_QUERY, this);
|
||||||
|
FUNCTION_TEST_PARAM(STRING, key);
|
||||||
|
FUNCTION_TEST_END();
|
||||||
|
|
||||||
|
ASSERT(this != NULL);
|
||||||
|
ASSERT(key != NULL);
|
||||||
|
|
||||||
|
FUNCTION_TEST_RETURN(this->redactList != NULL && strLstExists(this->redactList, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************************************************************************/
|
||||||
|
String *
|
||||||
|
httpQueryRender(const HttpQuery *this, HttpQueryRenderParam param)
|
||||||
|
{
|
||||||
|
FUNCTION_TEST_BEGIN();
|
||||||
|
FUNCTION_TEST_PARAM(HTTP_QUERY, this);
|
||||||
|
FUNCTION_TEST_PARAM(BOOL, param.redact);
|
||||||
FUNCTION_TEST_END();
|
FUNCTION_TEST_END();
|
||||||
|
|
||||||
String *result = NULL;
|
String *result = NULL;
|
||||||
|
|
||||||
if (this != NULL)
|
if (this != NULL)
|
||||||
|
{
|
||||||
|
MEM_CONTEXT_TEMP_BEGIN()
|
||||||
{
|
{
|
||||||
const StringList *keyList = httpQueryList(this);
|
const StringList *keyList = httpQueryList(this);
|
||||||
|
|
||||||
if (strLstSize(keyList) > 0)
|
if (strLstSize(keyList) > 0)
|
||||||
{
|
{
|
||||||
result = strNew("");
|
MEM_CONTEXT_PRIOR_BEGIN()
|
||||||
|
|
||||||
MEM_CONTEXT_TEMP_BEGIN()
|
|
||||||
{
|
{
|
||||||
|
result = strNew("");
|
||||||
|
}
|
||||||
|
MEM_CONTEXT_PRIOR_END();
|
||||||
|
|
||||||
for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)
|
for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)
|
||||||
{
|
{
|
||||||
|
const String *key = strLstGet(keyList, keyIdx);
|
||||||
|
|
||||||
if (strSize(result) != 0)
|
if (strSize(result) != 0)
|
||||||
strCatZ(result, "&");
|
strCatZ(result, "&");
|
||||||
|
|
||||||
strCatFmt(
|
strCatFmt(
|
||||||
result, "%s=%s", strPtr(strLstGet(keyList, keyIdx)),
|
result, "%s=%s", strPtr(key),
|
||||||
strPtr(httpUriEncode(httpQueryGet(this, strLstGet(keyList, keyIdx)), false)));
|
param.redact && httpQueryRedact(this, key) ?
|
||||||
|
"<redacted>" : strPtr(httpUriEncode(httpQueryGet(this, key), false)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MEM_CONTEXT_TEMP_END();
|
MEM_CONTEXT_TEMP_END();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
FUNCTION_TEST_RETURN(result);
|
FUNCTION_TEST_RETURN(result);
|
||||||
}
|
}
|
||||||
@ -276,12 +305,17 @@ httpQueryToLog(const HttpQuery *this)
|
|||||||
|
|
||||||
for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)
|
for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)
|
||||||
{
|
{
|
||||||
|
const String *key = strLstGet(keyList, keyIdx);
|
||||||
|
|
||||||
if (strSize(result) != 1)
|
if (strSize(result) != 1)
|
||||||
strCatZ(result, ", ");
|
strCatZ(result, ", ");
|
||||||
|
|
||||||
strCatFmt(
|
strCatFmt(result, "%s: ", strPtr(key));
|
||||||
result, "%s: '%s'", strPtr(strLstGet(keyList, keyIdx)),
|
|
||||||
strPtr(httpQueryGet(this, strLstGet(keyList, keyIdx))));
|
if (httpQueryRedact(this, key))
|
||||||
|
strCatZ(result, "<redacted>");
|
||||||
|
else
|
||||||
|
strCatFmt(result, "'%s'", strPtr(httpQueryGet(this, key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
strCatZ(result, "}");
|
strCatZ(result, "}");
|
||||||
|
@ -19,12 +19,31 @@ typedef struct HttpQuery HttpQuery;
|
|||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Constructors
|
Constructors
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
HttpQuery *httpQueryNew(void);
|
typedef struct HttpQueryNewParam
|
||||||
|
{
|
||||||
|
VAR_PARAM_HEADER;
|
||||||
|
const StringList *redactList; // List of keys to redact values for
|
||||||
|
} HttpQueryNewParam;
|
||||||
|
|
||||||
|
#define httpQueryNewP(...) \
|
||||||
|
httpQueryNew((HttpQueryNewParam){VAR_PARAM_INIT, __VA_ARGS__})
|
||||||
|
|
||||||
|
HttpQuery *httpQueryNew(HttpQueryNewParam param);
|
||||||
|
|
||||||
// New from encoded query string
|
// New from encoded query string
|
||||||
HttpQuery *httpQueryNewStr(const String *query);
|
HttpQuery *httpQueryNewStr(const String *query);
|
||||||
|
|
||||||
HttpQuery *httpQueryDup(const HttpQuery *query);
|
// Duplicate
|
||||||
|
typedef struct HttpQueryDupParam
|
||||||
|
{
|
||||||
|
VAR_PARAM_HEADER;
|
||||||
|
const StringList *redactList; // List of keys to redact values for
|
||||||
|
} HttpQueryDupParam;
|
||||||
|
|
||||||
|
#define httpQueryDupP(query, ...) \
|
||||||
|
httpQueryDup(query, (HttpQueryDupParam){VAR_PARAM_INIT, __VA_ARGS__})
|
||||||
|
|
||||||
|
HttpQuery *httpQueryDup(const HttpQuery *query, HttpQueryDupParam param);
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Functions
|
Functions
|
||||||
@ -47,8 +66,20 @@ HttpQuery *httpQueryMove(HttpQuery *this, MemContext *parentNew);
|
|||||||
// Put a query item
|
// Put a query item
|
||||||
HttpQuery *httpQueryPut(HttpQuery *this, const String *header, const String *value);
|
HttpQuery *httpQueryPut(HttpQuery *this, const String *header, const String *value);
|
||||||
|
|
||||||
|
// Should the query key be redacted when logging?
|
||||||
|
bool httpQueryRedact(const HttpQuery *this, const String *key);
|
||||||
|
|
||||||
// Render the query for inclusion in an HTTP request
|
// Render the query for inclusion in an HTTP request
|
||||||
String *httpQueryRender(const HttpQuery *this);
|
typedef struct HttpQueryRenderParam
|
||||||
|
{
|
||||||
|
VAR_PARAM_HEADER;
|
||||||
|
bool redact; // Redact user-visible query string
|
||||||
|
} HttpQueryRenderParam;
|
||||||
|
|
||||||
|
#define httpQueryRenderP(this, ...) \
|
||||||
|
httpQueryRender(this, (HttpQueryRenderParam){VAR_PARAM_INIT, __VA_ARGS__})
|
||||||
|
|
||||||
|
String *httpQueryRender(const HttpQuery *this, HttpQueryRenderParam param);
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Destructor
|
Destructor
|
||||||
|
@ -104,7 +104,7 @@ httpRequestProcess(HttpRequest *this, bool requestOnly, bool contentCache)
|
|||||||
String *requestStr =
|
String *requestStr =
|
||||||
strNewFmt(
|
strNewFmt(
|
||||||
"%s %s%s%s " HTTP_VERSION CRLF_Z, strPtr(this->verb), strPtr(httpUriEncode(this->uri, true)),
|
"%s %s%s%s " HTTP_VERSION CRLF_Z, strPtr(this->verb), strPtr(httpUriEncode(this->uri, true)),
|
||||||
this->query == NULL ? "" : "?", this->query == NULL ? "" : strPtr(httpQueryRender(this->query)));
|
this->query == NULL ? "" : "?", this->query == NULL ? "" : strPtr(httpQueryRenderP(this->query)));
|
||||||
|
|
||||||
// Add headers
|
// Add headers
|
||||||
const StringList *headerList = httpHeaderList(this->header);
|
const StringList *headerList = httpHeaderList(this->header);
|
||||||
@ -203,7 +203,7 @@ httpRequestNew(HttpClient *client, const String *verb, const String *uri, HttpRe
|
|||||||
.client = client,
|
.client = client,
|
||||||
.verb = strDup(verb),
|
.verb = strDup(verb),
|
||||||
.uri = strDup(uri),
|
.uri = strDup(uri),
|
||||||
.query = httpQueryDup(param.query),
|
.query = httpQueryDupP(param.query),
|
||||||
.header = param.header == NULL ? httpHeaderNew(NULL) : httpHeaderDup(param.header, NULL),
|
.header = param.header == NULL ? httpHeaderNew(NULL) : httpHeaderDup(param.header, NULL),
|
||||||
.content = param.content == NULL ? NULL : bufDup(param.content),
|
.content = param.content == NULL ? NULL : bufDup(param.content),
|
||||||
};
|
};
|
||||||
@ -256,7 +256,7 @@ httpRequestError(const HttpRequest *this, HttpResponse *response)
|
|||||||
strCatFmt(error, "\n%s", strPtr(httpUriEncode(this->uri, true)));
|
strCatFmt(error, "\n%s", strPtr(httpUriEncode(this->uri, true)));
|
||||||
|
|
||||||
if (this->query != NULL)
|
if (this->query != NULL)
|
||||||
strCatFmt(error, "?%s", strPtr(httpQueryRender(this->query)));
|
strCatFmt(error, "?%s", strPtr(httpQueryRenderP(this->query, .redact = true)));
|
||||||
|
|
||||||
// Output request headers
|
// Output request headers
|
||||||
const StringList *requestHeaderList = httpHeaderList(this->header);
|
const StringList *requestHeaderList = httpHeaderList(this->header);
|
||||||
|
@ -45,6 +45,7 @@ STRING_EXTERN(AZURE_QUERY_COMP_STR, AZURE_QUERY_
|
|||||||
STRING_STATIC(AZURE_QUERY_DELIMITER_STR, "delimiter");
|
STRING_STATIC(AZURE_QUERY_DELIMITER_STR, "delimiter");
|
||||||
STRING_STATIC(AZURE_QUERY_PREFIX_STR, "prefix");
|
STRING_STATIC(AZURE_QUERY_PREFIX_STR, "prefix");
|
||||||
STRING_EXTERN(AZURE_QUERY_RESTYPE_STR, AZURE_QUERY_RESTYPE);
|
STRING_EXTERN(AZURE_QUERY_RESTYPE_STR, AZURE_QUERY_RESTYPE);
|
||||||
|
STRING_STATIC(AZURE_QUERY_SIG_STR, "sig");
|
||||||
|
|
||||||
STRING_STATIC(AZURE_QUERY_VALUE_LIST_STR, "list");
|
STRING_STATIC(AZURE_QUERY_VALUE_LIST_STR, "list");
|
||||||
STRING_EXTERN(AZURE_QUERY_VALUE_CONTAINER_STR, AZURE_QUERY_VALUE_CONTAINER);
|
STRING_EXTERN(AZURE_QUERY_VALUE_CONTAINER_STR, AZURE_QUERY_VALUE_CONTAINER);
|
||||||
@ -70,6 +71,7 @@ struct StorageAzure
|
|||||||
MemContext *memContext;
|
MemContext *memContext;
|
||||||
HttpClient *httpClient; // Http client to service requests
|
HttpClient *httpClient; // Http client to service requests
|
||||||
StringList *headerRedactList; // List of headers to redact from logging
|
StringList *headerRedactList; // List of headers to redact from logging
|
||||||
|
StringList *queryRedactList; // List of query keys to redact from logging
|
||||||
|
|
||||||
const String *container; // Container to store data in
|
const String *container; // Container to store data in
|
||||||
const String *account; // Account
|
const String *account; // Account
|
||||||
@ -237,7 +239,10 @@ storageAzureRequestAsync(StorageAzure *this, const String *verb, StorageAzureReq
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make a copy of the query so it can be modified
|
// Make a copy of the query so it can be modified
|
||||||
HttpQuery *query = this->sasKey != NULL && param.query == NULL ? httpQueryNew() : httpQueryDup(param.query);
|
HttpQuery *query =
|
||||||
|
this->sasKey != NULL && param.query == NULL ?
|
||||||
|
httpQueryNewP(.redactList = this->queryRedactList) :
|
||||||
|
httpQueryDupP(param.query, .redactList = this->queryRedactList);
|
||||||
|
|
||||||
// Generate authorization header
|
// Generate authorization header
|
||||||
storageAzureAuth(this, verb, httpUriEncode(param.uri, true), query, httpDateFromTime(time(NULL)), requestHeader);
|
storageAzureAuth(this, verb, httpUriEncode(param.uri, true), query, httpDateFromTime(time(NULL)), requestHeader);
|
||||||
@ -363,7 +368,7 @@ storageAzureListInternal(
|
|||||||
// free memory at regular intervals
|
// free memory at regular intervals
|
||||||
MEM_CONTEXT_TEMP_BEGIN()
|
MEM_CONTEXT_TEMP_BEGIN()
|
||||||
{
|
{
|
||||||
HttpQuery *query = httpQueryNew();
|
HttpQuery *query = httpQueryNewP();
|
||||||
|
|
||||||
// Add continuation token from the prior loop if any
|
// Add continuation token from the prior loop if any
|
||||||
if (marker != NULL)
|
if (marker != NULL)
|
||||||
@ -742,6 +747,10 @@ storageAzureNew(
|
|||||||
strLstAdd(driver->headerRedactList, HTTP_HEADER_AUTHORIZATION_STR);
|
strLstAdd(driver->headerRedactList, HTTP_HEADER_AUTHORIZATION_STR);
|
||||||
strLstAdd(driver->headerRedactList, HTTP_HEADER_DATE_STR);
|
strLstAdd(driver->headerRedactList, HTTP_HEADER_DATE_STR);
|
||||||
|
|
||||||
|
// Create list of redacted query keys
|
||||||
|
driver->queryRedactList = strLstNew();
|
||||||
|
strLstAdd(driver->queryRedactList, AZURE_QUERY_SIG_STR);
|
||||||
|
|
||||||
// Generate starting file id
|
// Generate starting file id
|
||||||
cryptoRandomBytes((unsigned char *)&driver->fileId, sizeof(driver->fileId));
|
cryptoRandomBytes((unsigned char *)&driver->fileId, sizeof(driver->fileId));
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ storageWriteAzureBlockAsync(StorageWriteAzure *this)
|
|||||||
const String *blockId = strNewFmt("%016" PRIX64 "x%07u", this->fileId, strLstSize(this->blockIdList));
|
const String *blockId = strNewFmt("%016" PRIX64 "x%07u", this->fileId, strLstSize(this->blockIdList));
|
||||||
|
|
||||||
// Upload the block and add to block list
|
// Upload the block and add to block list
|
||||||
HttpQuery *query = httpQueryNew();
|
HttpQuery *query = httpQueryNewP();
|
||||||
httpQueryAdd(query, AZURE_QUERY_COMP_STR, AZURE_QUERY_VALUE_BLOCK_STR);
|
httpQueryAdd(query, AZURE_QUERY_COMP_STR, AZURE_QUERY_VALUE_BLOCK_STR);
|
||||||
httpQueryAdd(query, AZURE_QUERY_BLOCK_ID_STR, blockId);
|
httpQueryAdd(query, AZURE_QUERY_BLOCK_ID_STR, blockId);
|
||||||
|
|
||||||
@ -240,7 +240,7 @@ storageWriteAzureClose(THIS_VOID)
|
|||||||
// Finalize the multi-block upload
|
// Finalize the multi-block upload
|
||||||
storageAzureRequestP(
|
storageAzureRequestP(
|
||||||
this->storage, HTTP_VERB_PUT_STR, .uri = this->interface.name,
|
this->storage, HTTP_VERB_PUT_STR, .uri = this->interface.name,
|
||||||
.query = httpQueryAdd(httpQueryNew(), AZURE_QUERY_COMP_STR, AZURE_QUERY_VALUE_BLOCK_LIST_STR),
|
.query = httpQueryAdd(httpQueryNewP(), AZURE_QUERY_COMP_STR, AZURE_QUERY_VALUE_BLOCK_LIST_STR),
|
||||||
.content = xmlDocumentBuf(blockXml));
|
.content = xmlDocumentBuf(blockXml));
|
||||||
}
|
}
|
||||||
// Else upload all the data in a single block
|
// Else upload all the data in a single block
|
||||||
|
@ -173,7 +173,7 @@ storageS3Auth(
|
|||||||
String *signedHeaders = NULL;
|
String *signedHeaders = NULL;
|
||||||
|
|
||||||
String *canonicalRequest = strNewFmt(
|
String *canonicalRequest = strNewFmt(
|
||||||
"%s\n%s\n%s\n", strPtr(verb), strPtr(uri), query == NULL ? "" : strPtr(httpQueryRender(query)));
|
"%s\n%s\n%s\n", strPtr(verb), strPtr(uri), query == NULL ? "" : strPtr(httpQueryRenderP(query)));
|
||||||
|
|
||||||
for (unsigned int headerIdx = 0; headerIdx < strLstSize(headerList); headerIdx++)
|
for (unsigned int headerIdx = 0; headerIdx < strLstSize(headerList); headerIdx++)
|
||||||
{
|
{
|
||||||
@ -436,7 +436,7 @@ storageS3ListInternal(
|
|||||||
// free memory at regular intervals
|
// free memory at regular intervals
|
||||||
MEM_CONTEXT_TEMP_BEGIN()
|
MEM_CONTEXT_TEMP_BEGIN()
|
||||||
{
|
{
|
||||||
HttpQuery *query = httpQueryNew();
|
HttpQuery *query = httpQueryNewP();
|
||||||
|
|
||||||
// Add continuation token from the prior loop if any
|
// Add continuation token from the prior loop if any
|
||||||
if (continuationToken != NULL)
|
if (continuationToken != NULL)
|
||||||
@ -700,7 +700,7 @@ storageS3PathRemoveInternal(StorageS3 *this, XmlDocument *request)
|
|||||||
|
|
||||||
const Buffer *response = httpResponseContent(
|
const Buffer *response = httpResponseContent(
|
||||||
storageS3RequestP(
|
storageS3RequestP(
|
||||||
this, HTTP_VERB_POST_STR, FSLASH_STR, .query = httpQueryAdd(httpQueryNew(), S3_QUERY_DELETE_STR, EMPTY_STR),
|
this, HTTP_VERB_POST_STR, FSLASH_STR, .query = httpQueryAdd(httpQueryNewP(), S3_QUERY_DELETE_STR, EMPTY_STR),
|
||||||
.content = xmlDocumentBuf(request)));
|
.content = xmlDocumentBuf(request)));
|
||||||
|
|
||||||
// Nothing is returned when there are no errors
|
// Nothing is returned when there are no errors
|
||||||
|
@ -128,7 +128,7 @@ storageWriteS3PartAsync(StorageWriteS3 *this)
|
|||||||
httpResponseContent(
|
httpResponseContent(
|
||||||
storageS3RequestP(
|
storageS3RequestP(
|
||||||
this->storage, HTTP_VERB_POST_STR, this->interface.name,
|
this->storage, HTTP_VERB_POST_STR, this->interface.name,
|
||||||
.query = httpQueryAdd(httpQueryNew(), S3_QUERY_UPLOADS_STR, EMPTY_STR)))));
|
.query = httpQueryAdd(httpQueryNewP(), S3_QUERY_UPLOADS_STR, EMPTY_STR)))));
|
||||||
|
|
||||||
// Store the upload id
|
// Store the upload id
|
||||||
MEM_CONTEXT_BEGIN(this->memContext)
|
MEM_CONTEXT_BEGIN(this->memContext)
|
||||||
@ -140,7 +140,7 @@ storageWriteS3PartAsync(StorageWriteS3 *this)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Upload the part async
|
// Upload the part async
|
||||||
HttpQuery *query = httpQueryNew();
|
HttpQuery *query = httpQueryNewP();
|
||||||
httpQueryAdd(query, S3_QUERY_UPLOAD_ID_STR, this->uploadId);
|
httpQueryAdd(query, S3_QUERY_UPLOAD_ID_STR, this->uploadId);
|
||||||
httpQueryAdd(query, S3_QUERY_PART_NUMBER_STR, strNewFmt("%u", strLstSize(this->uploadPartList) + 1));
|
httpQueryAdd(query, S3_QUERY_PART_NUMBER_STR, strNewFmt("%u", strLstSize(this->uploadPartList) + 1));
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ storageWriteS3Close(THIS_VOID)
|
|||||||
// Finalize the multi-part upload
|
// Finalize the multi-part upload
|
||||||
storageS3RequestP(
|
storageS3RequestP(
|
||||||
this->storage, HTTP_VERB_POST_STR, this->interface.name,
|
this->storage, HTTP_VERB_POST_STR, this->interface.name,
|
||||||
.query = httpQueryAdd(httpQueryNew(), S3_QUERY_UPLOAD_ID_STR, this->uploadId),
|
.query = httpQueryAdd(httpQueryNewP(), S3_QUERY_UPLOAD_ID_STR, this->uploadId),
|
||||||
.content = xmlDocumentBuf(partList));
|
.content = xmlDocumentBuf(partList));
|
||||||
}
|
}
|
||||||
// Else upload all the data in a single put
|
// Else upload all the data in a single put
|
||||||
|
@ -105,30 +105,44 @@ testRun(void)
|
|||||||
|
|
||||||
MEM_CONTEXT_TEMP_BEGIN()
|
MEM_CONTEXT_TEMP_BEGIN()
|
||||||
{
|
{
|
||||||
TEST_ASSIGN(query, httpQueryNew(), "new query");
|
StringList *redactList = strLstNew();
|
||||||
|
strLstAdd(redactList, STRDEF("key2"));
|
||||||
|
|
||||||
|
TEST_ASSIGN(query, httpQueryNewP(.redactList = redactList), "new query");
|
||||||
|
|
||||||
TEST_RESULT_PTR(httpQueryMove(query, memContextPrior()), query, "move to new context");
|
TEST_RESULT_PTR(httpQueryMove(query, memContextPrior()), query, "move to new context");
|
||||||
TEST_RESULT_PTR(httpQueryMove(NULL, memContextPrior()), NULL, "move null to new context");
|
TEST_RESULT_PTR(httpQueryMove(NULL, memContextPrior()), NULL, "move null to new context");
|
||||||
}
|
}
|
||||||
MEM_CONTEXT_TEMP_END();
|
MEM_CONTEXT_TEMP_END();
|
||||||
|
|
||||||
TEST_RESULT_STR(httpQueryRender(NULL), NULL, "null query renders null");
|
TEST_RESULT_STR(httpQueryRenderP(NULL), NULL, "null query renders null");
|
||||||
TEST_RESULT_STR(httpQueryRender(query), NULL, "empty query renders null");
|
TEST_RESULT_STR(httpQueryRenderP(query), NULL, "empty query renders null");
|
||||||
|
|
||||||
TEST_RESULT_PTR(httpQueryAdd(query, strNew("key2"), strNew("value2")), query, "add query");
|
TEST_RESULT_PTR(httpQueryAdd(query, strNew("key2"), strNew("value2")), query, "add query");
|
||||||
TEST_ERROR(httpQueryAdd(query, strNew("key2"), strNew("value2")), AssertError, "key 'key2' already exists");
|
TEST_ERROR(httpQueryAdd(query, strNew("key2"), strNew("value2")), AssertError, "key 'key2' already exists");
|
||||||
TEST_RESULT_PTR(httpQueryPut(query, strNew("key2"), strNew("value2a")), query, "put query");
|
TEST_RESULT_PTR(httpQueryPut(query, strNew("key2"), strNew("value2a")), query, "put query");
|
||||||
TEST_RESULT_STR_Z(httpQueryRender(query), "key2=value2a", "render one query item");
|
TEST_RESULT_STR_Z(httpQueryRenderP(query), "key2=value2a", "render one query item");
|
||||||
|
|
||||||
TEST_RESULT_PTR(httpQueryAdd(query, strNew("key1"), strNew("value 1?")), query, "add query");
|
TEST_RESULT_PTR(httpQueryAdd(query, strNew("key1"), strNew("value 1?")), query, "add query");
|
||||||
TEST_RESULT_STR_Z(strLstJoin(httpQueryList(query), ", "), "key1, key2", "query list");
|
TEST_RESULT_STR_Z(strLstJoin(httpQueryList(query), ", "), "key1, key2", "query list");
|
||||||
TEST_RESULT_STR_Z(httpQueryRender(query), "key1=value%201%3F&key2=value2a", "render two query items");
|
TEST_RESULT_STR_Z(httpQueryRenderP(query), "key1=value%201%3F&key2=value2a", "render two query items");
|
||||||
|
TEST_RESULT_STR_Z(
|
||||||
|
httpQueryRenderP(query, .redact = true), "key1=value%201%3F&key2=<redacted>", "render two query items with redaction");
|
||||||
|
|
||||||
TEST_RESULT_STR_Z(httpQueryGet(query, strNew("key1")), "value 1?", "get value");
|
TEST_RESULT_STR_Z(httpQueryGet(query, strNew("key1")), "value 1?", "get value");
|
||||||
TEST_RESULT_STR_Z(httpQueryGet(query, strNew("key2")), "value2a", "get value");
|
TEST_RESULT_STR_Z(httpQueryGet(query, strNew("key2")), "value2a", "get value");
|
||||||
TEST_RESULT_STR(httpQueryGet(query, strNew(BOGUS_STR)), NULL, "get missing value");
|
TEST_RESULT_STR(httpQueryGet(query, strNew(BOGUS_STR)), NULL, "get missing value");
|
||||||
|
|
||||||
TEST_RESULT_STR_Z(httpQueryToLog(query), "{key1: 'value 1?', key2: 'value2a'}", "log output");
|
TEST_RESULT_STR_Z(httpQueryToLog(query), "{key1: 'value 1?', key2: <redacted>}", "log output");
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST_TITLE("dup query with redaction");
|
||||||
|
|
||||||
|
StringList *redactList = strLstNew();
|
||||||
|
strLstAdd(redactList, STRDEF("key1"));
|
||||||
|
|
||||||
|
TEST_ASSIGN(query, httpQueryDupP(query, .redactList = redactList), "dup query");
|
||||||
|
TEST_RESULT_STR_Z(httpQueryToLog(query), "{key1: <redacted>, key2: 'value2a'}", "log output");
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
TEST_TITLE("new query from string");
|
TEST_TITLE("new query from string");
|
||||||
@ -137,13 +151,13 @@ testRun(void)
|
|||||||
|
|
||||||
HttpQuery *query2 = NULL;
|
HttpQuery *query2 = NULL;
|
||||||
TEST_ASSIGN(query2, httpQueryNewStr(STRDEF("?a=%2Bb&c=d%3D")), "query from string");
|
TEST_ASSIGN(query2, httpQueryNewStr(STRDEF("?a=%2Bb&c=d%3D")), "query from string");
|
||||||
TEST_RESULT_STR_Z(httpQueryRender(query2), "a=%2Bb&c=d%3D", "render query");
|
TEST_RESULT_STR_Z(httpQueryRenderP(query2), "a=%2Bb&c=d%3D", "render query");
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
TEST_TITLE("merge queries");
|
TEST_TITLE("merge queries");
|
||||||
|
|
||||||
TEST_RESULT_STR_Z(
|
TEST_RESULT_STR_Z(
|
||||||
httpQueryRender(httpQueryMerge(query, query2)), "a=%2Bb&c=d%3D&key1=value%201%3F&key2=value2a", "render merge");
|
httpQueryRenderP(httpQueryMerge(query, query2)), "a=%2Bb&c=d%3D&key1=value%201%3F&key2=value2a", "render merge");
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
TEST_TITLE("free query");
|
TEST_TITLE("free query");
|
||||||
@ -352,7 +366,7 @@ testRun(void)
|
|||||||
HttpHeader *headerRequest = httpHeaderNew(NULL);
|
HttpHeader *headerRequest = httpHeaderNew(NULL);
|
||||||
httpHeaderAdd(headerRequest, strNew("host"), strNew("myhost.com"));
|
httpHeaderAdd(headerRequest, strNew("host"), strNew("myhost.com"));
|
||||||
|
|
||||||
HttpQuery *query = httpQueryNew();
|
HttpQuery *query = httpQueryNewP();
|
||||||
httpQueryAdd(query, strNew("name"), strNew("/path/A Z.txt"));
|
httpQueryAdd(query, strNew("name"), strNew("/path/A Z.txt"));
|
||||||
httpQueryAdd(query, strNew("type"), strNew("test"));
|
httpQueryAdd(query, strNew("type"), strNew("test"));
|
||||||
|
|
||||||
@ -376,7 +390,7 @@ testRun(void)
|
|||||||
TEST_RESULT_STR_Z(httpRequestVerb(request), "GET", "check request verb");
|
TEST_RESULT_STR_Z(httpRequestVerb(request), "GET", "check request verb");
|
||||||
TEST_RESULT_STR_Z(httpRequestUri(request), "/", "check request uri");
|
TEST_RESULT_STR_Z(httpRequestUri(request), "/", "check request uri");
|
||||||
TEST_RESULT_STR_Z(
|
TEST_RESULT_STR_Z(
|
||||||
httpQueryRender(httpRequestQuery(request)), "name=%2Fpath%2FA%20Z.txt&type=test", "check request query");
|
httpQueryRenderP(httpRequestQuery(request)), "name=%2Fpath%2FA%20Z.txt&type=test", "check request query");
|
||||||
TEST_RESULT_PTR_NE(httpRequestHeader(request), NULL, "check request headers");
|
TEST_RESULT_PTR_NE(httpRequestHeader(request), NULL, "check request headers");
|
||||||
|
|
||||||
TEST_RESULT_UINT(httpResponseCode(response), 200, "check response code");
|
TEST_RESULT_UINT(httpResponseCode(response), 200, "check response code");
|
||||||
@ -484,7 +498,7 @@ testRun(void)
|
|||||||
TEST_ASSIGN(
|
TEST_ASSIGN(
|
||||||
request,
|
request,
|
||||||
httpRequestNewP(
|
httpRequestNewP(
|
||||||
client, strNew("GET"), strNew("/"), .query = httpQueryAdd(httpQueryNew(), STRDEF("a"), STRDEF("b")),
|
client, strNew("GET"), strNew("/"), .query = httpQueryAdd(httpQueryNewP(), STRDEF("a"), STRDEF("b")),
|
||||||
.header = headerRequest),
|
.header = headerRequest),
|
||||||
"request");
|
"request");
|
||||||
TEST_ASSIGN(response, httpRequest(request, false), "response");
|
TEST_ASSIGN(response, httpRequest(request, false), "response");
|
||||||
|
@ -44,7 +44,7 @@ testRequest(const char *verb, const char *uri, TestRequestParam param)
|
|||||||
// When SAS spit out the query and merge in the SAS key
|
// When SAS spit out the query and merge in the SAS key
|
||||||
if (driver->sasKey != NULL)
|
if (driver->sasKey != NULL)
|
||||||
{
|
{
|
||||||
HttpQuery *query = httpQueryNew();
|
HttpQuery *query = httpQueryNewP();
|
||||||
StringList *uriQuery = strLstNewSplitZ(STR(uri), "?");
|
StringList *uriQuery = strLstNewSplitZ(STR(uri), "?");
|
||||||
|
|
||||||
if (strLstSize(uriQuery) == 2)
|
if (strLstSize(uriQuery) == 2)
|
||||||
@ -54,7 +54,7 @@ testRequest(const char *verb, const char *uri, TestRequestParam param)
|
|||||||
|
|
||||||
strCat(request, strLstGet(uriQuery, 0));
|
strCat(request, strLstGet(uriQuery, 0));
|
||||||
strCatZ(request, "?");
|
strCatZ(request, "?");
|
||||||
strCat(request, httpQueryRender(query));
|
strCat(request, httpQueryRenderP(query));
|
||||||
}
|
}
|
||||||
// Else just output URI as is
|
// Else just output URI as is
|
||||||
else
|
else
|
||||||
@ -235,7 +235,7 @@ testRun(void)
|
|||||||
header = httpHeaderAdd(httpHeaderNew(NULL), HTTP_HEADER_CONTENT_LENGTH_STR, STRDEF("44"));
|
header = httpHeaderAdd(httpHeaderNew(NULL), HTTP_HEADER_CONTENT_LENGTH_STR, STRDEF("44"));
|
||||||
httpHeaderAdd(header, HTTP_HEADER_CONTENT_MD5_STR, STRDEF("b64f49553d5c441652e95697a2c5949e"));
|
httpHeaderAdd(header, HTTP_HEADER_CONTENT_MD5_STR, STRDEF("b64f49553d5c441652e95697a2c5949e"));
|
||||||
|
|
||||||
HttpQuery *query = httpQueryAdd(httpQueryNew(), STRDEF("a"), STRDEF("b"));
|
HttpQuery *query = httpQueryAdd(httpQueryNewP(), STRDEF("a"), STRDEF("b"));
|
||||||
|
|
||||||
TEST_RESULT_VOID(storageAzureAuth(storage, HTTP_VERB_GET_STR, STRDEF("/path/file"), query, dateTime, header), "auth");
|
TEST_RESULT_VOID(storageAzureAuth(storage, HTTP_VERB_GET_STR, STRDEF("/path/file"), query, dateTime, header), "auth");
|
||||||
TEST_RESULT_STR_Z(
|
TEST_RESULT_STR_Z(
|
||||||
@ -256,13 +256,13 @@ testRun(void)
|
|||||||
16, NULL, 443, 1000, true, NULL, NULL)),
|
16, NULL, 443, 1000, true, NULL, NULL)),
|
||||||
"new azure storage - sas key");
|
"new azure storage - sas key");
|
||||||
|
|
||||||
query = httpQueryAdd(httpQueryNew(), STRDEF("a"), STRDEF("b"));
|
query = httpQueryAdd(httpQueryNewP(), STRDEF("a"), STRDEF("b"));
|
||||||
header = httpHeaderAdd(httpHeaderNew(NULL), HTTP_HEADER_CONTENT_LENGTH_STR, STRDEF("66"));
|
header = httpHeaderAdd(httpHeaderNew(NULL), HTTP_HEADER_CONTENT_LENGTH_STR, STRDEF("66"));
|
||||||
|
|
||||||
TEST_RESULT_VOID(storageAzureAuth(storage, HTTP_VERB_GET_STR, STRDEF("/path/file"), query, dateTime, header), "auth");
|
TEST_RESULT_VOID(storageAzureAuth(storage, HTTP_VERB_GET_STR, STRDEF("/path/file"), query, dateTime, header), "auth");
|
||||||
TEST_RESULT_STR_Z(
|
TEST_RESULT_STR_Z(
|
||||||
httpHeaderToLog(header), "{content-length: '66', host: 'account.blob.core.windows.net'}", "check headers");
|
httpHeaderToLog(header), "{content-length: '66', host: 'account.blob.core.windows.net'}", "check headers");
|
||||||
TEST_RESULT_STR_Z(httpQueryRender(query), "a=b&sig=key", "check query");
|
TEST_RESULT_STR_Z(httpQueryRenderP(query), "a=b&sig=key", "check query");
|
||||||
}
|
}
|
||||||
|
|
||||||
// *****************************************************************************************************************************
|
// *****************************************************************************************************************************
|
||||||
@ -742,6 +742,22 @@ testRun(void)
|
|||||||
|
|
||||||
TEST_RESULT_VOID(storageRemoveP(storage, strNew("/path/to/missing.txt")), "remove");
|
TEST_RESULT_VOID(storageRemoveP(storage, strNew("/path/to/missing.txt")), "remove");
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST_TITLE("remove files error to check redacted sig");
|
||||||
|
|
||||||
|
testRequestP(HTTP_VERB_GET, "?comp=list&restype=container");
|
||||||
|
testResponseP(.code = 403);
|
||||||
|
|
||||||
|
TEST_ERROR_FMT(
|
||||||
|
storagePathRemoveP(storage, strNew("/"), .recurse = true), ProtocolError,
|
||||||
|
"HTTP request failed with 403 (Forbidden):\n"
|
||||||
|
"*** URI/Query ***:\n"
|
||||||
|
"/account/container?comp=list&restype=container&sig=<redacted>\n"
|
||||||
|
"*** Request Headers ***:\n"
|
||||||
|
"content-length: 0\n"
|
||||||
|
"host: %s",
|
||||||
|
strPtr(hrnTlsServerHost()));
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
TEST_TITLE("remove files from root");
|
TEST_TITLE("remove files from root");
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ testRun(void)
|
|||||||
|
|
||||||
HttpHeader *header = httpHeaderNew(NULL);
|
HttpHeader *header = httpHeaderNew(NULL);
|
||||||
|
|
||||||
HttpQuery *query = httpQueryNew();
|
HttpQuery *query = httpQueryNewP();
|
||||||
httpQueryAdd(query, strNew("list-type"), strNew("2"));
|
httpQueryAdd(query, strNew("list-type"), strNew("2"));
|
||||||
|
|
||||||
TEST_RESULT_VOID(
|
TEST_RESULT_VOID(
|
||||||
|
Reference in New Issue
Block a user