Skip to content

Commit

Permalink
feat: wallet open options for webkms & EDV
Browse files Browse the repository at this point in the history
- added more unlock wallet options to use all aries webkms & EDV
customization opts.
- can be used to pass auth & authz based http headers for EDV & web kms
- closes hyperledger-archives#2763

Signed-off-by: sudesh.shetty <sudesh.shetty@securekey.com>
  • Loading branch information
sudeshrshetty committed Apr 28, 2021
1 parent bc063ba commit 61315a4
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 57 deletions.
4 changes: 2 additions & 2 deletions pkg/wallet/contents.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ func newContentStore(p storage.Provider, pr *profile) *contentStore {
return &contentStore{open: storeLocked, close: noOp, provider: newWalletStorageProvider(pr, p)}
}

func (cs *contentStore) Open(auth string) error {
store, err := cs.provider.OpenStore(auth, storage.StoreConfiguration{TagNames: []string{
func (cs *contentStore) Open(auth string, opts *unlockOpts) error {
store, err := cs.provider.OpenStore(auth, opts, storage.StoreConfiguration{TagNames: []string{
Collection.Name(), Credential.Name(), Connection.Name(), DIDResolutionResponse.Name(), Connection.Name(), Key.Name(),
}})
if err != nil {
Expand Down
103 changes: 76 additions & 27 deletions pkg/wallet/contents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/require"

"github.com/hyperledger/aries-framework-go/component/storage/edv"
mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms"
mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
"github.com/hyperledger/aries-framework-go/pkg/mock/vdr"
Expand Down Expand Up @@ -194,7 +195,7 @@ func TestContentStores(t *testing.T) {
require.Empty(t, sp.config.TagNames)

// open store
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))
require.EqualValues(t, sp.config.TagNames,
[]string{"collection", "credential", "connection", "didResolutionResponse", "connection", "key"})

Expand All @@ -205,22 +206,70 @@ func TestContentStores(t *testing.T) {
require.True(t, errors.Is(err, ErrWalletLocked))
})

t.Run("create new content store for EDV profile - success", func(t *testing.T) {
sp := getMockStorageProvider()

masterLock, err := getDefaultSecretLock(samplePassPhrase)
require.NoError(t, err)

masterLockCipherText, err := createMasterLock(masterLock)
require.NoError(t, err)
require.NotEmpty(t, masterLockCipherText)

profileInfo := &profile{
ID: uuid.New().String(),
User: uuid.New().String(),
MasterLockCipher: masterLockCipherText,
EDVConf: &edvConf{
ServerURL: sampleEDVServerURL,
VaultID: sampleEDVVaultID,
},
}

tkn, err := keyManager().createKeyManager(profileInfo, sp, &unlockOpts{passphrase: samplePassPhrase})
require.NoError(t, err)
require.NotEmpty(t, tkn)

ok, err := profileInfo.setupEDVKeys(tkn, "", "")
require.NoError(t, err)
require.True(t, ok)

// create new store
contentStore := newContentStore(sp, profileInfo)
require.NotEmpty(t, contentStore)
require.Empty(t, sp.config.TagNames)

// open store
require.NoError(t, contentStore.Open(tkn, &unlockOpts{
edvOpts: []edv.RESTProviderOption{
edv.WithFullDocumentsReturnedFromQueries(),
edv.WithBatchEndpointExtension(),
},
}))

// close store
contentStore.Close()
store, err := contentStore.open(tkn)
require.Empty(t, store)
require.True(t, errors.Is(err, ErrWalletLocked))
})

t.Run("open store - failure", func(t *testing.T) {
sp := getMockStorageProvider()

contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})

// open store error
sp.ErrOpenStoreHandle = errors.New(sampleContenttErr)
err := contentStore.Open(token)
err := contentStore.Open(token, &unlockOpts{})
require.Error(t, err)
require.Contains(t, err.Error(), sampleContenttErr)
require.Contains(t, err.Error(), "failed to open store")

// set store config error
sp.ErrOpenStoreHandle = nil
sp.failure = errors.New(sampleContenttErr)
err = contentStore.Open(token)
err = contentStore.Open(token, &unlockOpts{})
require.Error(t, err)
require.Contains(t, err.Error(), sampleContenttErr)
require.Contains(t, err.Error(), "failed to set store config")
Expand All @@ -229,7 +278,7 @@ func TestContentStores(t *testing.T) {
sp.failure = nil
sp.Store.ErrClose = errors.New(sampleContenttErr)
contentStore = newContentStore(sp, &profile{ID: uuid.New().String()})
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

contentStore.Close()
})
Expand All @@ -240,7 +289,7 @@ func TestContentStores(t *testing.T) {
contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, nil))

err := contentStore.Save(token, Collection, []byte(sampleContentValid))
require.NoError(t, err)
Expand All @@ -260,7 +309,7 @@ func TestContentStores(t *testing.T) {
contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, nil))

err := contentStore.Save(token, Collection, []byte(sampleContentNoID))
require.NoError(t, err)
Expand All @@ -272,7 +321,7 @@ func TestContentStores(t *testing.T) {
contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

err := contentStore.Save(token, DIDResolutionResponse, []byte(didResolutionResult))
require.NoError(t, err)
Expand Down Expand Up @@ -386,7 +435,7 @@ func TestContentStores(t *testing.T) {
err = contentStore.Save(token, Credential, []byte(sampleContentValid))
require.True(t, errors.Is(err, ErrWalletLocked))

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

err = contentStore.Save(token, Credential, []byte(sampleContentValid))
require.Error(t, err)
Expand Down Expand Up @@ -415,7 +464,7 @@ func TestContentStores(t *testing.T) {
contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

err := contentStore.Save(token, Collection, []byte(sampleContentValid))
require.NoError(t, err)
Expand All @@ -432,7 +481,7 @@ func TestContentStores(t *testing.T) {
contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

// save
err := contentStore.Save(token, Collection, []byte(sampleContentValid))
Expand All @@ -455,7 +504,7 @@ func TestContentStores(t *testing.T) {
require.Empty(t, content)
require.True(t, errors.Is(err, ErrWalletLocked))

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

// get
content, err = contentStore.Get(token, "did:example:123456789abcdefghi", Collection)
Expand All @@ -470,7 +519,7 @@ func TestContentStores(t *testing.T) {
contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

// save
err := contentStore.Save(token, Collection, []byte(sampleContentValid))
Expand Down Expand Up @@ -499,7 +548,7 @@ func TestContentStores(t *testing.T) {
contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

// save
err := contentStore.Save(token, Collection, []byte(sampleContentValid))
Expand Down Expand Up @@ -558,7 +607,7 @@ func TestContentStore_GetAll(t *testing.T) {
contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

// save test data
const count = 5
Expand Down Expand Up @@ -598,7 +647,7 @@ func TestContentStore_GetAll(t *testing.T) {
require.True(t, errors.Is(err, ErrWalletLocked))
require.Empty(t, allVcs)

require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))
require.NoError(t, contentStore.Save(token, Credential, []byte(fmt.Sprintf(vcContent, uuid.New().String()))))

// iterator value error
Expand All @@ -613,7 +662,7 @@ func TestContentStore_GetAll(t *testing.T) {

contentStore = newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

allVcs, err = contentStore.GetAll(token, Credential)
require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrKey))
Expand All @@ -624,7 +673,7 @@ func TestContentStore_GetAll(t *testing.T) {

contentStore = newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

require.NoError(t, contentStore.Save(token, Credential, []byte(fmt.Sprintf(vcContent, uuid.New().String()))))

Expand All @@ -637,7 +686,7 @@ func TestContentStore_GetAll(t *testing.T) {

contentStore = newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

allVcs, err = contentStore.GetAll(token, Credential)
require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrQuery))
Expand All @@ -655,7 +704,7 @@ func TestContentDIDResolver(t *testing.T) {

contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

// save custom DID
err := contentStore.Save(token, DIDResolutionResponse, []byte(sampleDocResolutionResponse))
Expand Down Expand Up @@ -691,7 +740,7 @@ func TestContentDIDResolver(t *testing.T) {
require.Empty(t, didDoc)

// open store
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

// DID not found
didDoc, err = contentVDR.Resolve("did:key:invalid")
Expand Down Expand Up @@ -778,7 +827,7 @@ func TestContentStore_Collections(t *testing.T) {

contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

// save a collection
require.NoError(t, contentStore.Save(token, Collection, []byte(orgCollection)))
Expand Down Expand Up @@ -844,7 +893,7 @@ func TestContentStore_Collections(t *testing.T) {

contentStore := newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

err := contentStore.Save(token,
DIDResolutionResponse, []byte(didResolutionResult), AddByCollection(collectionID+"invalid"))
Expand All @@ -866,7 +915,7 @@ func TestContentStore_Collections(t *testing.T) {

contentStore = newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

allVcs, err := contentStore.GetAllByCollection(token, collectionID, Credential)
require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrGet))
Expand All @@ -877,7 +926,7 @@ func TestContentStore_Collections(t *testing.T) {

contentStore = newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

allVcs, err = contentStore.GetAllByCollection(token, collectionID, Credential)
require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrValue))
Expand All @@ -888,7 +937,7 @@ func TestContentStore_Collections(t *testing.T) {

contentStore = newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

allVcs, err = contentStore.GetAllByCollection(token, collectionID, Credential)
require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrKey))
Expand All @@ -899,7 +948,7 @@ func TestContentStore_Collections(t *testing.T) {

contentStore = newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

allVcs, err = contentStore.GetAllByCollection(token, collectionID, Credential)
require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrNext))
Expand All @@ -910,7 +959,7 @@ func TestContentStore_Collections(t *testing.T) {

contentStore = newContentStore(sp, &profile{ID: uuid.New().String()})
require.NotEmpty(t, contentStore)
require.NoError(t, contentStore.Open(token))
require.NoError(t, contentStore.Open(token, &unlockOpts{}))

allVcs, err = contentStore.GetAllByCollection(token, collectionID, Credential)
require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrQuery))
Expand Down
18 changes: 12 additions & 6 deletions pkg/wallet/kmsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (k *walletKeyManager) createKeyManager(profileInfo *profile,
}
} else {
// remote kms
keyManager = createRemoteKeyManager(opts.authToken, profileInfo.KeyServerURL)
keyManager = createRemoteKeyManager(opts, profileInfo.KeyServerURL)
}

// generate token
Expand Down Expand Up @@ -232,12 +232,18 @@ func getDefaultSecretLock(passphrase string) (secretlock.Service, error) {
}

// createLocalKeyManager creates and returns remote KMS instance.
func createRemoteKeyManager(auth, keyServerURL string) *webkms.RemoteKMS {
return webkms.New(keyServerURL, http.DefaultClient, webkms.WithHeaders(func(req *http.Request) (*http.Header, error) {
req.Header.Set("authorization", fmt.Sprintf("Bearer %s", auth))
func createRemoteKeyManager(opts *unlockOpts, keyServerURL string) *webkms.RemoteKMS {
kmsOpts := opts.webkmsOpts

return &req.Header, nil
}))
if opts.authToken != "" {
kmsOpts = append(kmsOpts, webkms.WithHeaders(func(req *http.Request) (*http.Header, error) {
req.Header.Set("authorization", fmt.Sprintf("Bearer %s", opts.authToken))

return &req.Header, nil
}))
}

return webkms.New(keyServerURL, http.DefaultClient, kmsOpts...)
}

type kmsSigner struct {
Expand Down
24 changes: 23 additions & 1 deletion pkg/wallet/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
"encoding/json"
"time"

"github.com/hyperledger/aries-framework-go/component/storage/edv"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
"github.com/hyperledger/aries-framework-go/pkg/kms/webkms"
"github.com/hyperledger/aries-framework-go/pkg/secretlock"
)

Expand Down Expand Up @@ -73,10 +75,14 @@ type unlockOpts struct {
secretLockSvc secretlock.Service

// remote(web) kms options
authToken string
authToken string
webkmsOpts []webkms.Opt

// expiry
tokenExpiry time.Duration

// edv opts
edvOpts []edv.RESTProviderOption
}

// UnlockOptions is option for unlocking verifiable credential wallet key manager.
Expand Down Expand Up @@ -117,6 +123,22 @@ func WithUnlockExpiry(tokenExpiry time.Duration) UnlockOptions {
}
}

// WithUnlockWebKMSOptions can be used to provide custom aries web kms options for unlocking wallet.
// This option can be used to set web kms client http header function instead of using WithUnlockByAuthorizationToken.
func WithUnlockWebKMSOptions(webkmsOpts ...webkms.Opt) UnlockOptions {
return func(opts *unlockOpts) {
opts.webkmsOpts = webkmsOpts
}
}

// WithUnlockEDVOptions can be used to provide custom aries edv options for unlocking wallet.
// Provided options will be considered only if given wallet profile is using EDV configurations.
func WithUnlockEDVOptions(edvOpts ...edv.RESTProviderOption) UnlockOptions {
return func(opts *unlockOpts) {
opts.edvOpts = edvOpts
}
}

// proveOpts contains options for proving credentials.
type proveOpts struct {
// IDs of credentials already saved in wallet.
Expand Down

0 comments on commit 61315a4

Please sign in to comment.