From e20b6145719f349ddf704f62d748e0cb187b7cce Mon Sep 17 00:00:00 2001 From: Derek Trider Date: Sat, 28 Nov 2020 21:29:28 -0500 Subject: [PATCH] feat: Add option to EDV REST provider to get full document on query - Added an option to enable a performance optimization that can be enabled if being used with an EDV server that supports it. - Removed a comment with a ToDo regarding dealing with a potential edge-case issue since I've now realized that the design of the EDV provider already prevents it from happening in the first place. Signed-off-by: Derek Trider --- pkg/storage/edv/models/models.go | 6 +- pkg/storage/edv/restprovider.go | 127 ++++++++++--- pkg/storage/edv/restprovider_test.go | 177 +++++++++++++++--- .../formattedstore/formattedstore_test.go | 2 +- 4 files changed, 258 insertions(+), 54 deletions(-) diff --git a/pkg/storage/edv/models/models.go b/pkg/storage/edv/models/models.go index 369bd1ea55..e7320cc4c9 100644 --- a/pkg/storage/edv/models/models.go +++ b/pkg/storage/edv/models/models.go @@ -54,7 +54,9 @@ type IDTypePair struct { // Query represents a name+value pair that can be used to query the encrypted indices for specific data. // TODO: #2262 This is a simplified version of the actual EDV query format, which is still not finalized // in the spec as of writing. See: https://github.com/decentralized-identity/secure-data-store/issues/34. +// ReturnFullDocuments is currently non-standard and should only be used with an EDV server that supports it. type Query struct { - Name string `json:"index"` - Value string `json:"equals"` + ReturnFullDocuments bool `json:"returnFullDocuments"` + Name string `json:"index"` + Value string `json:"equals"` } diff --git a/pkg/storage/edv/restprovider.go b/pkg/storage/edv/restprovider.go index 1b9d128c21..a6c2ec7b82 100644 --- a/pkg/storage/edv/restprovider.go +++ b/pkg/storage/edv/restprovider.go @@ -40,6 +40,7 @@ const ( failCreateIndexedAttributes = "failed to create indexed attributes: %w" failComputeBase64EncodedStoreAndKeyIndexValueMAC = "failed to compute Base64-encoded store+key index value MAC: %w" failCreateDocumentInEDVServer = "failed to create document in EDV server: %w" + failGetFullDocumentViaQuery = "failed to get full document via query: %w" failUpdateDocumentInEDVServer = "failed to update existing document in EDV server: %w" failRetrieveEDVDocumentID = "failed to retrieve EDV document ID: %w" failQueryVaultInEDVServer = "failed to query vault in EDV server: %w" @@ -49,14 +50,15 @@ const ( failGetAllDocumentLocations = "failed to get all document locations: %w" failGetAllDocuments = "failed to get all documents: %w" - failSendGETRequest = "failed to send GET request: %w" - failSendPOSTRequest = "failed to send POST request: %w" - failCreateRequest = "failed to create request: %w" - failSendRequest = "failed to send request: %w" - failReadResponseBody = "failed to read response body: %w" - failMarshalQuery = "failed to marshal query: %w" - failResponseFromEDVServer = "status code %d was returned along with the following message: %s" - failUnmarshalDocumentLocations = "failed to unmarshal response bytes into document locations: %w" + failSendGETRequest = "failed to send GET request: %w" + failSendPOSTRequest = "failed to send POST request: %w" + failCreateRequest = "failed to create request: %w" + failSendRequest = "failed to send request: %w" + failReadResponseBody = "failed to read response body: %w" + failMarshalQuery = "failed to marshal query: %w" + failUnmarshalEncryptedDocuments = "failed to unmarshal encrypted documents: %w" + failResponseFromEDVServer = "status code %d was returned along with the following message: %s" + failUnmarshalDocumentLocations = "failed to unmarshal response bytes into document locations: %w" createDocumentRequestLogMsg = "Sending request to create the following document: %s" updateDocumentRequestLogMsg = "Sending request to update the following document: %s" @@ -103,6 +105,11 @@ type RESTProvider struct { storeIndexNameMACBase64Encoded string storeAndKeyIndexNameMACBase64Encoded string restClient restClient + + // Requires an EDV server that supports this capability, which is not currently in the spec, + // but has been requested: https://github.com/decentralized-identity/confidential-storage/issues/137. + // If enabled, allows for the Put method to execute faster by reducing the number of REST calls from 2 down to 1. + returnFullDocumentsOnQuery bool } // NewRESTProvider returns a new RESTProvider. edvServerURL is the base URL for the data vault HTTPS API. @@ -111,8 +118,10 @@ type RESTProvider struct { // non-existent vault will be deferred until calls are actually made to it in the restStore. // macCrypto is used to create an encrypted indices, which allow for documents to be queries based on a key // without leaking that key to the EDV server. +// If the EDV server you're using supports returning full documents in query results instead of only the document +// locations, then returnFullDocumentsOnQuery can be set to true for a performance improvement in Get calls. func NewRESTProvider(edvServerURL, vaultID string, - macCrypto *MACCrypto, httpClientOpts ...Option) (*RESTProvider, error) { + macCrypto *MACCrypto, returnFullDocumentsOnQuery bool, httpClientOpts ...Option) (*RESTProvider, error) { storeAndKeyIndexNameMAC, err := macCrypto.ComputeMAC(storeAndKeyIndexName) if err != nil { return nil, fmt.Errorf(failComputeMACStoreAndKeyIndexName, err) @@ -138,6 +147,7 @@ func NewRESTProvider(edvServerURL, vaultID string, storeIndexNameMACBase64Encoded: base64.URLEncoding.EncodeToString([]byte(storeIndexNameMAC)), storeAndKeyIndexNameMACBase64Encoded: base64.URLEncoding.EncodeToString([]byte(storeAndKeyIndexNameMAC)), restClient: client, + returnFullDocumentsOnQuery: returnFullDocumentsOnQuery, }, nil } @@ -150,6 +160,7 @@ func (r *RESTProvider) OpenStore(name string) (storage.Store, error) { storeIndexNameMACBase64Encoded: r.storeIndexNameMACBase64Encoded, storeAndKeyIndexNameMACBase64Encoded: r.storeAndKeyIndexNameMACBase64Encoded, restClient: r.restClient, + returnFullDocumentsOnQuery: r.returnFullDocumentsOnQuery, }, nil } @@ -170,6 +181,7 @@ type restStore struct { macCrypto *MACCrypto storeIndexNameMACBase64Encoded string storeAndKeyIndexNameMACBase64Encoded string + returnFullDocumentsOnQuery bool } // v must be a marshalled EncryptedDocument. @@ -183,7 +195,7 @@ func (r *restStore) Put(k string, v []byte) error { } // If existingEDVDocumentID was set, then this means that there is already an existing document that - // well get updated. + // we need to update. err = r.createEDVDocument(k, v, existingEDVDocumentID) if err != nil { return fmt.Errorf(failStoreEDVDocument, err) @@ -193,6 +205,16 @@ func (r *restStore) Put(k string, v []byte) error { } func (r *restStore) Get(k string) ([]byte, error) { + if r.returnFullDocumentsOnQuery { + // Take a shortcut and get the full document from the query in one REST call. + encryptedDocumentBytes, err := r.getFullDocumentViaQuery(k) + if err != nil { + return nil, fmt.Errorf(failGetFullDocumentViaQuery, err) + } + + return encryptedDocumentBytes, nil + } + // Get document ID from query, then do another call to get the full document. edvDocumentID, err := r.retrieveEDVDocumentID(k) if err != nil { return nil, fmt.Errorf(failRetrieveEDVDocumentID, err) @@ -305,16 +327,40 @@ func (r *restStore) createIndexedAttributes(keyName string) ([]models.IndexedAtt return indexedAttributeCollections, nil } +func (r *restStore) getFullDocumentViaQuery(k string) ([]byte, error) { + storeAndKeyIndexValueMACBase64Encoded, err := r.computeStoreAndKeyIndexValueMACBase64Encoded(k) + if err != nil { + return nil, fmt.Errorf(failComputeBase64EncodedStoreAndKeyIndexValueMAC, err) + } + + matchingDocuments, err := r.restClient.queryVaultForFullDocuments(r.vaultID, + r.storeAndKeyIndexNameMACBase64Encoded, storeAndKeyIndexValueMACBase64Encoded) + if err != nil { + return nil, fmt.Errorf(failQueryVaultInEDVServer, err) + } + + if len(matchingDocuments) == 0 { + return nil, fmt.Errorf(noDocumentMatchingQueryFound, storage.ErrDataNotFound) + } else if len(matchingDocuments) > 1 { + return nil, errMultipleDocumentsMatchingQuery + } + + encryptedDocumentBytes, err := json.Marshal(matchingDocuments[0]) + if err != nil { + return nil, fmt.Errorf(failMarshalEncryptedDocument, err) + } + + return encryptedDocumentBytes, nil +} + func (r *restStore) retrieveEDVDocumentID(k string) (string, error) { storeAndKeyIndexValueMACBase64Encoded, err := r.computeStoreAndKeyIndexValueMACBase64Encoded(k) if err != nil { return "", fmt.Errorf(failComputeBase64EncodedStoreAndKeyIndexValueMAC, err) } - matchingDocumentURLs, err := r.restClient.queryVault(r.vaultID, &models.Query{ - Name: r.storeAndKeyIndexNameMACBase64Encoded, - Value: storeAndKeyIndexValueMACBase64Encoded, - }) + matchingDocumentURLs, err := r.restClient.queryVault(r.vaultID, + r.storeAndKeyIndexNameMACBase64Encoded, storeAndKeyIndexValueMACBase64Encoded) if err != nil { return "", fmt.Errorf(failQueryVaultInEDVServer, err) } @@ -322,10 +368,6 @@ func (r *restStore) retrieveEDVDocumentID(k string) (string, error) { if len(matchingDocumentURLs) == 0 { return "", fmt.Errorf(noDocumentMatchingQueryFound, storage.ErrDataNotFound) } else if len(matchingDocumentURLs) > 1 { - // This should only be possible if the EDV server is not able to maintain the uniqueness property of the - // storeAndKeyIndexName indexedAttribute created in the createIndexedAttributes method. - // TODO (#2287): Check each of the documents to see if they all have the same content - // (other than the document ID). If so, we should delete the extras and just return one of them arbitrarily. return "", errMultipleDocumentsMatchingQuery } @@ -338,10 +380,8 @@ func (r *restStore) getAllDocumentLocations() ([]string, error) { return nil, fmt.Errorf(failComputeBase64EncodedStoreAndKeyIndexValueMAC, err) } - allDocumentLocations, err := r.restClient.queryVault(r.vaultID, &models.Query{ - Name: r.storeIndexNameMACBase64Encoded, - Value: storeNameIndexValueMACBase64Encoded, - }) + allDocumentLocations, err := r.restClient.queryVault(r.vaultID, + r.storeIndexNameMACBase64Encoded, storeNameIndexValueMACBase64Encoded) if err != nil { return nil, fmt.Errorf(failQueryVaultInEDVServer, err) } @@ -538,7 +578,13 @@ func (c *restClient) readDocument(vaultID, docID string) ([]byte, error) { } // queryVault queries the given vault and returns the URLs of all documents that match the given query. -func (c *restClient) queryVault(vaultID string, query *models.Query) ([]string, error) { +func (c *restClient) queryVault(vaultID, name, value string) ([]string, error) { + query := models.Query{ + ReturnFullDocuments: false, + Name: name, + Value: value, + } + jsonToSend, err := json.Marshal(query) if err != nil { return nil, fmt.Errorf(failMarshalQuery, err) @@ -565,6 +611,41 @@ func (c *restClient) queryVault(vaultID string, query *models.Query) ([]string, return nil, fmt.Errorf(failResponseFromEDVServer, statusCode, respBytes) } +// queryVaultForFullDocuments queries the given vault and returns all documents that match the given query. +// Requires the EDV server to support this functionality, which is currently non-standard. +func (c *restClient) queryVaultForFullDocuments(vaultID, name, value string) ([]models.EncryptedDocument, error) { + query := models.Query{ + ReturnFullDocuments: false, + Name: name, + Value: value, + } + + jsonToSend, err := json.Marshal(query) + if err != nil { + return nil, fmt.Errorf(failMarshalQuery, err) + } + + endpoint := fmt.Sprintf("%s/%s/query", c.edvServerURL, url.PathEscape(vaultID)) + + statusCode, _, respBytes, err := c.sendHTTPRequest(http.MethodPost, endpoint, jsonToSend, c.headersFunc) + if err != nil { + return nil, fmt.Errorf(failSendPOSTRequest, err) + } + + if statusCode == http.StatusOK { + var documents []models.EncryptedDocument + + err = json.Unmarshal(respBytes, &documents) + if err != nil { + return nil, fmt.Errorf(failUnmarshalEncryptedDocuments, err) + } + + return documents, nil + } + + return nil, fmt.Errorf(failResponseFromEDVServer, statusCode, respBytes) +} + // DeleteDocument sends the EDV server a request to delete the specified document. func (c *restClient) DeleteDocument(vaultID, docID string) error { endpoint := fmt.Sprintf("%s/%s/documents/%s", c.edvServerURL, url.PathEscape(vaultID), url.PathEscape(docID)) diff --git a/pkg/storage/edv/restprovider_test.go b/pkg/storage/edv/restprovider_test.go index 38b3b92ddd..0f1d749de9 100644 --- a/pkg/storage/edv/restprovider_test.go +++ b/pkg/storage/edv/restprovider_test.go @@ -33,18 +33,18 @@ const ( func TestNewRESTProvider(t *testing.T) { t.Run("Success", func(t *testing.T) { - createRESTProvider(t, "EDVServerURL") + createRESTProvider("EDVServerURL", t, false) }) t.Run("Fail to compute index name MAC", func(t *testing.T) { provider, err := NewRESTProvider("EDVServerURL", "vaultID", - NewMACCrypto(nil, &mockcrypto.Crypto{ComputeMACErr: errTest})) + NewMACCrypto(nil, &mockcrypto.Crypto{ComputeMACErr: errTest}), false) require.EqualError(t, err, fmt.Errorf(failComputeMACStoreAndKeyIndexName, errTest).Error()) require.Nil(t, provider) }) } func TestRESTProvider_OpenStore(t *testing.T) { - provider := createRESTProvider(t, "EDVServerURL") + provider := createRESTProvider("EDVServerURL", t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -52,14 +52,14 @@ func TestRESTProvider_OpenStore(t *testing.T) { } func TestRESTProvider_CloseStore(t *testing.T) { - provider := createRESTProvider(t, "EDVServerURL") + provider := createRESTProvider("EDVServerURL", t, false) err := provider.CloseStore("StoreName") require.NoError(t, err) } func TestRESTProvider_Close(t *testing.T) { - provider := createRESTProvider(t, "EDVServerURL") + provider := createRESTProvider("EDVServerURL", t, false) err := provider.Close() require.NoError(t, err) @@ -85,7 +85,7 @@ func TestRestStore_Put(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -110,7 +110,7 @@ func TestRestStore_Put(t *testing.T) { require.NoError(t, err) }) t.Run("Test error from injecting header", func(t *testing.T) { - provider := createRESTProvider(t, "EDVServerURL") + provider := createRESTProvider("EDVServerURL", t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -131,7 +131,7 @@ func TestRestStore_Put(t *testing.T) { require.Contains(t, err.Error(), "failed to add header") }) t.Run("Fail to check for existing document", func(t *testing.T) { - provider := createRESTProvider(t, "EDVServerURL") + provider := createRESTProvider("EDVServerURL", t, false) provider.macCrypto = NewMACCrypto(nil, &mockcrypto.Crypto{ComputeMACErr: errTest}) store, err := provider.OpenStore("StoreName") @@ -163,7 +163,7 @@ func TestRestStore_Put(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -191,7 +191,7 @@ func TestRestStore_Put(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -228,7 +228,7 @@ func TestRestStore_Put(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -278,7 +278,7 @@ func TestRestStore_Get(t *testing.T) { edvSrv := mockEDVServerOp.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -288,8 +288,52 @@ func TestRestStore_Get(t *testing.T) { require.NoError(t, err) require.Equal(t, string(encryptedDocumentBytes), string(encryptedDocumentBytesFromServer)) }) + t.Run(`Success, with the "return full documents on query" option enabled`, func(t *testing.T) { + encryptedDocumentToReturnFromQuery := models.EncryptedDocument{ID: sampleEncryptedDocumentID} + queryResults := []models.EncryptedDocument{encryptedDocumentToReturnFromQuery} + + queryResultsBytes, err := json.Marshal(queryResults) + require.NoError(t, err) + + mockEDVServerOp := edv.MockServerOperation{ + T: t, + QueryVaultReturnStatusCode: http.StatusOK, + QueryVaultReturnBody: queryResultsBytes, + } + edvSrv := mockEDVServerOp.StartNewMockEDVServer() + defer edvSrv.Close() + + provider := createRESTProvider(edvSrv.URL, t, true) + + store, err := provider.OpenStore("StoreName") + require.NoError(t, err) + require.NotNil(t, store) + + encryptedDocumentBytesFromServer, err := store.Get(testKey) + require.NoError(t, err) + + encryptedDocumentToReturnFromQueryBytes, err := json.Marshal(encryptedDocumentToReturnFromQuery) + require.NoError(t, err) + + require.Equal(t, string(encryptedDocumentToReturnFromQueryBytes), string(encryptedDocumentBytesFromServer)) + }) + t.Run("Fail to get full document via query", func(t *testing.T) { + provider := createRESTProvider("EDVServerURL", t, true) + provider.macCrypto = NewMACCrypto(nil, &mockcrypto.Crypto{ComputeMACErr: errTest}) + + store, err := provider.OpenStore("StoreName") + require.NoError(t, err) + require.NotNil(t, store) + + encryptedDocumentBytes, err := store.Get(testKey) + require.EqualError(t, err, + fmt.Errorf(failGetFullDocumentViaQuery, + fmt.Errorf(failComputeBase64EncodedStoreAndKeyIndexValueMAC, + fmt.Errorf(failComputeMACStoreAndKeyIndexValue, errTest))).Error()) + require.Nil(t, encryptedDocumentBytes) + }) t.Run("Fail to compute Base64 encoded index value MAC", func(t *testing.T) { - provider := createRESTProvider(t, "EDVServerURL") + provider := createRESTProvider("EDVServerURL", t, false) provider.macCrypto = NewMACCrypto(nil, &mockcrypto.Crypto{ComputeMACErr: errTest}) store, err := provider.OpenStore("StoreName") @@ -303,7 +347,7 @@ func TestRestStore_Get(t *testing.T) { fmt.Errorf(failComputeMACStoreAndKeyIndexValue, errTest))).Error()) require.Nil(t, encryptedDocumentBytes) }) - t.Run("Receive error response from EDV server query endpoint", func(t *testing.T) { + t.Run("Error response from query endpoint", func(t *testing.T) { mockEDVServerOperation := edv.MockServerOperation{ T: t, QueryVaultReturnStatusCode: http.StatusInternalServerError, @@ -312,7 +356,7 @@ func TestRestStore_Get(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -325,7 +369,29 @@ func TestRestStore_Get(t *testing.T) { fmt.Errorf(failResponseFromEDVServer, http.StatusInternalServerError, errTest.Error()))).Error()) require.Nil(t, encryptedDocumentBytes) }) - t.Run("No document was found matching the query", func(t *testing.T) { + t.Run(`Error response from query endpoint (using "return full docs on query" option)`, func(t *testing.T) { + mockEDVServerOperation := edv.MockServerOperation{ + T: t, + QueryVaultReturnStatusCode: http.StatusInternalServerError, + QueryVaultReturnBody: []byte(errTest.Error()), + } + edvSrv := mockEDVServerOperation.StartNewMockEDVServer() + defer edvSrv.Close() + + provider := createRESTProvider(edvSrv.URL, t, true) + + store, err := provider.OpenStore("StoreName") + require.NoError(t, err) + require.NotNil(t, store) + + encryptedDocumentBytes, err := store.Get(testKey) + require.EqualError(t, err, + fmt.Errorf(failGetFullDocumentViaQuery, + fmt.Errorf(failQueryVaultInEDVServer, + fmt.Errorf(failResponseFromEDVServer, http.StatusInternalServerError, errTest.Error()))).Error()) + require.Nil(t, encryptedDocumentBytes) + }) + t.Run("No doc was found matching the query", func(t *testing.T) { queryResults := make([]string, 0) queryResultsBytes, err := json.Marshal(queryResults) @@ -339,7 +405,7 @@ func TestRestStore_Get(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -351,7 +417,33 @@ func TestRestStore_Get(t *testing.T) { fmt.Errorf(noDocumentMatchingQueryFound, storage.ErrDataNotFound)).Error()) require.Nil(t, encryptedDocumentBytes) }) - t.Run("Multiple documents found matching the query", func(t *testing.T) { + t.Run(`No doc was found matching the query (using "return full docs on query" option)`, func(t *testing.T) { + queryResults := make([]string, 0) + + queryResultsBytes, err := json.Marshal(queryResults) + require.NoError(t, err) + + mockEDVServerOperation := edv.MockServerOperation{ + T: t, + QueryVaultReturnStatusCode: http.StatusOK, + QueryVaultReturnBody: queryResultsBytes, + } + edvSrv := mockEDVServerOperation.StartNewMockEDVServer() + defer edvSrv.Close() + + provider := createRESTProvider(edvSrv.URL, t, true) + + store, err := provider.OpenStore("StoreName") + require.NoError(t, err) + require.NotNil(t, store) + + encryptedDocumentBytes, err := store.Get(testKey) + require.EqualError(t, err, + fmt.Errorf(failGetFullDocumentViaQuery, + fmt.Errorf(noDocumentMatchingQueryFound, storage.ErrDataNotFound)).Error()) + require.Nil(t, encryptedDocumentBytes) + }) + t.Run("Two docs found matching the query", func(t *testing.T) { queryResults := []string{ "https://example.com/encrypted-data-vaults/z4sRgBJJLnYy/docs/zMbxmSDn2Xzz", "https://example.com/encrypted-data-vaults/z4sRgBJJLnYy/docs/AJYHHJx4C8J9Fsgz7rZqSp", @@ -368,7 +460,7 @@ func TestRestStore_Get(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -380,6 +472,35 @@ func TestRestStore_Get(t *testing.T) { errMultipleDocumentsMatchingQuery).Error()) require.Nil(t, encryptedDocumentBytes) }) + t.Run(`Two docs found matching the query (using "return full docs on query" option)`, func(t *testing.T) { + encryptedDoc1 := models.EncryptedDocument{ID: "docID1"} + encryptedDoc2 := models.EncryptedDocument{ID: "docID2"} + + queryResults := []models.EncryptedDocument{encryptedDoc1, encryptedDoc2} + + queryResultsBytes, err := json.Marshal(queryResults) + require.NoError(t, err) + + mockEDVServerOperation := edv.MockServerOperation{ + T: t, + QueryVaultReturnStatusCode: http.StatusOK, + QueryVaultReturnBody: queryResultsBytes, + } + edvSrv := mockEDVServerOperation.StartNewMockEDVServer() + defer edvSrv.Close() + + provider := createRESTProvider(edvSrv.URL, t, true) + + store, err := provider.OpenStore("StoreName") + require.NoError(t, err) + require.NotNil(t, store) + + encryptedDocumentBytes, err := store.Get(testKey) + require.EqualError(t, err, + fmt.Errorf(failGetFullDocumentViaQuery, + errMultipleDocumentsMatchingQuery).Error()) + require.Nil(t, encryptedDocumentBytes) + }) t.Run("Receive error response from EDV server delete document endpoint", func(t *testing.T) { queryResults := []string{"z19x9iFMnfo4YLsShKAvnJk4L"} @@ -396,7 +517,7 @@ func TestRestStore_Get(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -429,7 +550,7 @@ func TestRestStore_Iterator(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -454,7 +575,7 @@ func TestRestStore_Iterator(t *testing.T) { verifyIterator(t, iterator, len(keys)) }) t.Run("Fail to get all document locations", func(t *testing.T) { - provider := createRESTProvider(t, "EDVServerURL") + provider := createRESTProvider("EDVServerURL", t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -486,7 +607,7 @@ func TestRestStore_Iterator(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -528,7 +649,7 @@ func TestRestStore_Delete(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -556,7 +677,7 @@ func TestRestStore_Delete(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -582,7 +703,7 @@ func TestRestStore_Delete(t *testing.T) { edvSrv := mockEDVServerOperation.StartNewMockEDVServer() defer edvSrv.Close() - provider := createRESTProvider(t, edvSrv.URL) + provider := createRESTProvider(edvSrv.URL, t, false) store, err := provider.OpenStore("StoreName") require.NoError(t, err) @@ -619,8 +740,8 @@ func TestRestStore_CreateEDVDocument(t *testing.T) { }) } -func createRESTProvider(t *testing.T, edvServerURL string) *RESTProvider { - provider, err := NewRESTProvider(edvServerURL, "vaultID", newMACCrypto(t), +func createRESTProvider(edvServerURL string, t *testing.T, returnFullDocumentsOnQuery bool) *RESTProvider { + provider, err := NewRESTProvider(edvServerURL, "vaultID", newMACCrypto(t), returnFullDocumentsOnQuery, WithTLSConfig(&tls.Config{ServerName: "name", MinVersion: tls.VersionTLS13}), WithHeaders(func(req *http.Request) (*http.Header, error) { req.Header.Set("h1", "v1") diff --git a/pkg/storage/formattedstore/formattedstore_test.go b/pkg/storage/formattedstore/formattedstore_test.go index 9e1a5a4797..7cac2c58fb 100644 --- a/pkg/storage/formattedstore/formattedstore_test.go +++ b/pkg/storage/formattedstore/formattedstore_test.go @@ -613,7 +613,7 @@ func createEncrypterAndDecrypter(t *testing.T) (*jose.JWEEncrypt, *jose.JWEDecry } func createEDVRESTProvider(t *testing.T, edvServerURL string) *edv.RESTProvider { - provider, err := edv.NewRESTProvider(edvServerURL, "vaultID", newMACCrypto(t), + provider, err := edv.NewRESTProvider(edvServerURL, "vaultID", newMACCrypto(t), false, edv.WithTLSConfig(&tls.Config{ServerName: "name", MinVersion: tls.VersionTLS13})) require.NoError(t, err) require.NotNil(t, provider)