rebase: bump github.com/IBM/keyprotect-go-client from 0.7.0 to 0.8.0

Bumps [github.com/IBM/keyprotect-go-client](https://github.com/IBM/keyprotect-go-client) from 0.7.0 to 0.8.0.
- [Release notes](https://github.com/IBM/keyprotect-go-client/releases)
- [Commits](https://github.com/IBM/keyprotect-go-client/compare/v0.7.0...v0.8.0)

---
updated-dependencies:
- dependency-name: github.com/IBM/keyprotect-go-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot] 2022-06-27 20:15:13 +00:00 committed by mergify[bot]
parent d6719c4d62
commit 4b709310e2
17 changed files with 721 additions and 168 deletions

6
go.mod
View File

@ -3,7 +3,7 @@ module github.com/ceph/ceph-csi
go 1.17 go 1.17
require ( require (
github.com/IBM/keyprotect-go-client v0.7.0 github.com/IBM/keyprotect-go-client v0.8.0
github.com/aws/aws-sdk-go v1.44.28 github.com/aws/aws-sdk-go v1.44.28
github.com/aws/aws-sdk-go-v2/service/sts v1.16.7 github.com/aws/aws-sdk-go-v2/service/sts v1.16.7
github.com/ceph/ceph-csi/api v0.0.0-00010101000000-000000000000 github.com/ceph/ceph-csi/api v0.0.0-00010101000000-000000000000
@ -75,7 +75,7 @@ require (
github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.8 // indirect github.com/google/go-cmp v0.5.8 // indirect
github.com/google/gofuzz v1.1.0 // indirect github.com/google/gofuzz v1.1.0 // indirect
github.com/google/uuid v1.1.2 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@ -83,7 +83,7 @@ require (
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.4.3 // indirect github.com/hashicorp/go-plugin v1.4.3 // indirect
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect

13
go.sum
View File

@ -79,8 +79,8 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
github.com/DataDog/zstd v1.4.4/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.4/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/GoogleCloudPlatform/k8s-cloud-provider v1.16.1-0.20210702024009-ea6160c1d0e3/go.mod h1:8XasY4ymP2V/tn2OOV9ZadmiTE1FIB/h3W+yNlPttKw= github.com/GoogleCloudPlatform/k8s-cloud-provider v1.16.1-0.20210702024009-ea6160c1d0e3/go.mod h1:8XasY4ymP2V/tn2OOV9ZadmiTE1FIB/h3W+yNlPttKw=
github.com/IBM/keyprotect-go-client v0.5.1/go.mod h1:5TwDM/4FRJq1ZOlwQL1xFahLWQ3TveR88VmL1u3njyI= github.com/IBM/keyprotect-go-client v0.5.1/go.mod h1:5TwDM/4FRJq1ZOlwQL1xFahLWQ3TveR88VmL1u3njyI=
github.com/IBM/keyprotect-go-client v0.7.0 h1:JstSHD14Lp6ihwQseyPuGcs1AjOBjAmcisP0dTBA6A0= github.com/IBM/keyprotect-go-client v0.8.0 h1:IgLKSigHRpCCl5cZjBkOYziUZ9zxn6w9iRh+KA8Siww=
github.com/IBM/keyprotect-go-client v0.7.0/go.mod h1:SVr2ylV/fhSQPDiUjWirN9fsyWFCNNbt8GIT8hPJVjE= github.com/IBM/keyprotect-go-client v0.8.0/go.mod h1:yr8h2noNgU8vcbs+vhqoXp3Lmv73PI0zAc6VMgFvWwM=
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA= github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E= github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
@ -494,8 +494,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
@ -582,8 +583,9 @@ github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a/go.mod
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY=
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
@ -1695,8 +1697,9 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@ -119,11 +119,11 @@ crkID := key.ID
myDEK := []byte{"thisisadataencryptionkey"} myDEK := []byte{"thisisadataencryptionkey"}
// Do some encryption with myDEK // Do some encryption with myDEK
// Wrap the DEK so we can safely store it // Wrap the DEK so we can safely store it
wrappedDEK, err := client.Wrap(ctx, crkID, myDEK, nil) wrappedDEK, err := client.Wrap(ctx, crkIDOrAlias, myDEK, nil)
// Unwrap the DEK // Unwrap the DEK
dek, err := client.Unwrap(ctx, crkID, wrappedDEK, nil) dek, err := client.Unwrap(ctx, crkIDOrAlias, wrappedDEK, nil)
// Do some encryption/decryption using the DEK // Do some encryption/decryption using the DEK
// Discard the DEK // Discard the DEK
dek = nil dek = nil
@ -138,11 +138,11 @@ myAAD := []string{"First aad string", "second aad string", "third aad string"}
myDEK := []byte{"thisisadataencryptionkey"} myDEK := []byte{"thisisadataencryptionkey"}
// Do some encryption with myDEK // Do some encryption with myDEK
// Wrap the DEK so we can safely store it // Wrap the DEK so we can safely store it
wrappedDEK, err := client.Wrap(ctx, crkID, myDEK, &myAAD) wrappedDEK, err := client.Wrap(ctx, crkIDOrAlias, myDEK, &myAAD)
// Unwrap the DEK // Unwrap the DEK
dek, err := client.Unwrap(ctx, crkID, wrappedDEK, &myAAD) dek, err := client.Unwrap(ctx, crkIDOrAlias, wrappedDEK, &myAAD)
// Do some encryption/decryption using the DEK // Do some encryption/decryption using the DEK
// Discard the DEK // Discard the DEK
dek = nil dek = nil
@ -151,7 +151,7 @@ dek = nil
Have key protect create a DEK for you: Have key protect create a DEK for you:
```go ```go
dek, wrappedDek, err := client.WrapCreateDEK(ctx, crkID, nil) dek, wrappedDek, err := client.WrapCreateDEK(ctx, crkIDOrAlias, nil)
// Do some encrypt/decrypt with the dek // Do some encrypt/decrypt with the dek
// Discard the DEK // Discard the DEK
dek = nil dek = nil
@ -163,7 +163,7 @@ Can also specify AAD:
```go ```go
myAAD := []string{"First aad string", "second aad string", "third aad string"} myAAD := []string{"First aad string", "second aad string", "third aad string"}
dek, wrappedDek, err := client.WrapCreateDEK(ctx, crkID, &myAAD) dek, wrappedDek, err := client.WrapCreateDEK(ctx, crkIDOrAlias, &myAAD)
// Do some encrypt/decrypt with the dek // Do some encrypt/decrypt with the dek
// Discard the DEK // Discard the DEK
dek = nil dek = nil
@ -171,3 +171,46 @@ dek = nil
// Save the wrapped DEK for later. Call Unwrap to use it, make // Save the wrapped DEK for later. Call Unwrap to use it, make
// sure to specify the same AAD. // sure to specify the same AAD.
``` ```
### Fetching List Key Versions With Parameters.
```go
limit := uint32(2)
offset := uint32(0)
totalCount := true
listkeyVersionsOptions := &kp.ListKeyVersionsOptions{
Limit : &limit,
Offset : &offset,
TotalCount : &totalCount,
}
keyVersions, err := client.ListKeyVersions(ctx, "key_id_or_alias", listkeyVersionsOptions)
if err != nil {
fmt.Println(err)
}
fmt.Println(keyVersions)
```
### Fetching List Key With Parameters.
```go
limit := uint32(5)
offset := uint32(0)
extractable := false
keyStates := []kp.KeyState{kp.KeyState(kp.Active), kp.KeyState(kp.Suspended)}
listKeysOptions := &kp.ListKeysOptions{
Limit : &limit,
Offset : &offset,
Extractable : &extractable,
State : keyStates,
}
keys, err := client.ListKeys(ctx, listKeysOptions)
if err != nil {
fmt.Println(err)
}
fmt.Println(keys)
```

View File

@ -32,7 +32,13 @@ import (
"time" "time"
) )
const importTokenEncAlgo = "RSAES_OAEP_SHA_256" // currently the only one supported // EncryptionAlgorithm represents the encryption algorithm used for key creation
const (
// AlgorithmRSAOAEP256 denotes RSA OAEP SHA 256 encryption, supported by KP
AlgorithmRSAOAEP256 string = "RSAES_OAEP_SHA_256"
// AlgorithmRSAOAEP1 denotes RSA OAEP SHA 1 encryption, supported by HPCS
AlgorithmRSAOAEP1 string = "RSAES_OAEP_SHA_1"
)
// ImportTokenCreateRequest represents request parameters for creating a // ImportTokenCreateRequest represents request parameters for creating a
// ImportToken. // ImportToken.

View File

@ -33,9 +33,9 @@ type KeyAliases struct {
// An alias name acts as an identifier just like key ID // An alias name acts as an identifier just like key ID
// For more information please refer to the link below: // For more information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-create-key-alias#create-key-alias-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-create-key-alias#create-key-alias-api
func (c *Client) CreateKeyAlias(ctx context.Context, aliasName, keyID string) (*KeyAlias, error) { func (c *Client) CreateKeyAlias(ctx context.Context, aliasName, idOrAlias string) (*KeyAlias, error) {
req, err := c.newRequest("POST", fmt.Sprintf(requestPath, keyID, aliasName), nil) req, err := c.newRequest("POST", fmt.Sprintf(requestPath, idOrAlias, aliasName), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -56,9 +56,9 @@ func (c *Client) CreateKeyAlias(ctx context.Context, aliasName, keyID string) (*
// DeleteKeyAlias deletes an alias name associated with a key // DeleteKeyAlias deletes an alias name associated with a key
// For more information please refer to the link below: // For more information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-create-key-alias#delete-key-alias // https://cloud.ibm.com/docs/key-protect?topic=key-protect-create-key-alias#delete-key-alias
func (c *Client) DeleteKeyAlias(ctx context.Context, aliasName, keyID string) error { func (c *Client) DeleteKeyAlias(ctx context.Context, aliasName, idOrAlias string) error {
req, err := c.newRequest("DELETE", fmt.Sprintf(requestPath, keyID, aliasName), nil) req, err := c.newRequest("DELETE", fmt.Sprintf(requestPath, idOrAlias, aliasName), nil)
if err != nil { if err != nil {
return err return err
} }

View File

@ -21,6 +21,7 @@ import (
"log" "log"
"net/url" "net/url"
"strconv" "strconv"
"strings"
"time" "time"
) )
@ -38,6 +39,17 @@ var (
// PreferReturn designates the value for the "Prefer" header. // PreferReturn designates the value for the "Prefer" header.
type PreferReturn int type PreferReturn int
type KeyState uint32
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-key-states
const (
Active KeyState = iota + 1
Suspended
Deactivated
_
Destroyed
)
// Key represents a key as returned by the KP API. // Key represents a key as returned by the KP API.
type Key struct { type Key struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
@ -65,6 +77,9 @@ type Key struct {
Deleted *bool `json:"deleted,omitempty"` Deleted *bool `json:"deleted,omitempty"`
DeletedBy *string `json:"deletedBy,omitempty"` DeletedBy *string `json:"deletedBy,omitempty"`
DeletionDate *time.Time `json:"deletionDate,omitempty"` DeletionDate *time.Time `json:"deletionDate,omitempty"`
PurgeAllowed *bool `json:"purgeAllowed,omitempty"`
PurgeAllowedFrom *time.Time `json:"purgeAllowedFrom,omitempty"`
PurgeScheduledOn *time.Time `json:"purgeScheduledOn,omitempty"`
DualAuthDelete *DualAuth `json:"dualAuthDelete,omitempty"` DualAuthDelete *DualAuth `json:"dualAuthDelete,omitempty"`
} }
@ -80,13 +95,27 @@ type Keys struct {
Keys []Key `json:"resources"` Keys []Key `json:"resources"`
} }
type KeyVersionsMetadata struct {
CollectionType string `json:"collectionType"`
CollectionTotal *uint32 `json:"collectionTotal"`
TotalCount *uint32 `json:"totalCount,omitempty"`
}
type KeyVersions struct {
Metadata KeyVersionsMetadata `json:"metadata"`
KeyVersion []KeyVersion `json:"resources"`
}
// KeysActionRequest represents request parameters for a key action // KeysActionRequest represents request parameters for a key action
// API call. // API call.
type KeysActionRequest struct { type KeysActionRequest struct {
PlainText string `json:"plaintext,omitempty"` PlainText string `json:"plaintext,omitempty"`
AAD []string `json:"aad,omitempty"` AAD []string `json:"aad,omitempty"`
CipherText string `json:"ciphertext,omitempty"` CipherText string `json:"ciphertext,omitempty"`
Payload string `json:"payload,omitempty"` Payload string `json:"payload,omitempty"`
EncryptedNonce string `json:"encryptedNonce,omitempty"`
IV string `json:"iv,omitempty"`
EncryptionAlgorithm string `json:"encryptionAlgorithm,omitempty"`
} }
type KeyVersion struct { type KeyVersion struct {
@ -101,23 +130,14 @@ func (c *Client) CreateKey(ctx context.Context, name string, expiration *time.Ti
// CreateImportedKey creates a new KP key from the given key material. // CreateImportedKey creates a new KP key from the given key material.
func (c *Client) CreateImportedKey(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool) (*Key, error) { func (c *Client) CreateImportedKey(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool) (*Key, error) {
key := Key{ key := c.createKeyTemplate(ctx, name, expiration, payload, encryptedNonce, iv, extractable, nil, AlgorithmRSAOAEP256)
Name: name, return c.createKey(ctx, key)
Type: keyType, }
Extractable: extractable,
Payload: payload,
}
if payload != "" && encryptedNonce != "" && iv != "" {
key.EncryptedNonce = encryptedNonce
key.IV = iv
key.EncryptionAlgorithm = importTokenEncAlgo
}
if expiration != nil {
key.Expiration = expiration
}
// CreateImportedKeyWithSHA1 creates a new KP key from the given key material
// using RSAES OAEP SHA 1 as encryption algorithm.
func (c *Client) CreateImportedKeyWithSHA1(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool, aliases []string) (*Key, error) {
key := c.createKeyTemplate(ctx, name, expiration, payload, encryptedNonce, iv, extractable, aliases, AlgorithmRSAOAEP1)
return c.createKey(ctx, key) return c.createKey(ctx, key)
} }
@ -160,25 +180,33 @@ func (c *Client) CreateKeyWithAliases(ctx context.Context, name string, expirati
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-import-root-keys#import-root-key-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-import-root-keys#import-root-key-api
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-import-standard-keys#import-standard-key-gui // https://cloud.ibm.com/docs/key-protect?topic=key-protect-import-standard-keys#import-standard-key-gui
func (c *Client) CreateImportedKeyWithAliases(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool, aliases []string) (*Key, error) { func (c *Client) CreateImportedKeyWithAliases(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool, aliases []string) (*Key, error) {
key := c.createKeyTemplate(ctx, name, expiration, payload, encryptedNonce, iv, extractable, aliases, AlgorithmRSAOAEP256)
return c.createKey(ctx, key)
}
func (c *Client) createKeyTemplate(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool, aliases []string, encryptionAlgorithm string) Key {
key := Key{ key := Key{
Name: name, Name: name,
Type: keyType, Type: keyType,
Extractable: extractable, Extractable: extractable,
Payload: payload, Payload: payload,
Aliases: aliases, }
if aliases != nil {
key.Aliases = aliases
} }
if !extractable && payload != "" && encryptedNonce != "" && iv != "" { if !extractable && payload != "" && encryptedNonce != "" && iv != "" {
key.EncryptedNonce = encryptedNonce key.EncryptedNonce = encryptedNonce
key.IV = iv key.IV = iv
key.EncryptionAlgorithm = importTokenEncAlgo key.EncryptionAlgorithm = encryptionAlgorithm
} }
if expiration != nil { if expiration != nil {
key.Expiration = expiration key.Expiration = expiration
} }
return c.createKey(ctx, key) return key
} }
func (c *Client) createKey(ctx context.Context, key Key) (*Key, error) { func (c *Client) createKey(ctx context.Context, key Key) (*Key, error) {
@ -203,6 +231,36 @@ func (c *Client) createKey(ctx context.Context, key Key) (*Key, error) {
return &keysResponse.Keys[0], nil return &keysResponse.Keys[0], nil
} }
// SetKeyRing method transfers a key associated with one key ring to another key ring
// For more information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-grouping-keys#transfer-key-key-ring
func (c *Client) SetKeyRing(ctx context.Context, idOrAlias, newKeyRingID string) (*Key, error) {
if idOrAlias == "" {
return nil, fmt.Errorf("Please provide a valid key ID or alias")
}
if newKeyRingID == "" {
return nil, fmt.Errorf("Please provide a valid key ring id")
}
keyRingRequestBody := struct {
KeyRingID string
}{
KeyRingID: newKeyRingID,
}
req, err := c.newRequest("PATCH", fmt.Sprintf("keys/%s", idOrAlias), keyRingRequestBody)
if err != nil {
return nil, err
}
response := Keys{}
if _, err := c.do(ctx, req, &response); err != nil {
return nil, err
}
return &response.Keys[0], nil
}
// GetKeys retrieves a collection of keys that can be paged through. // GetKeys retrieves a collection of keys that can be paged through.
func (c *Client) GetKeys(ctx context.Context, limit int, offset int) (*Keys, error) { func (c *Client) GetKeys(ctx context.Context, limit int, offset int) (*Keys, error) {
if limit == 0 { if limit == 0 {
@ -228,6 +286,55 @@ func (c *Client) GetKeys(ctx context.Context, limit int, offset int) (*Keys, err
return &keys, nil return &keys, nil
} }
//ListKeysOptions struct to add the query parameters for the List Keys function
type ListKeysOptions struct {
Extractable *bool
Limit *uint32
Offset *uint32
State []KeyState
}
// ListKeys retrieves a list of keys that are stored in your Key Protect service instance.
// https://cloud.ibm.com/apidocs/key-protect#getkeys
func (c *Client) ListKeys(ctx context.Context, listKeysOptions *ListKeysOptions) (*Keys, error) {
req, err := c.newRequest("GET", "keys", nil)
if err != nil {
return nil, err
}
// extracting the query parameters and encoding the same in the request url
if listKeysOptions != nil {
values := req.URL.Query()
if listKeysOptions.Limit != nil {
values.Set("limit", fmt.Sprint(*listKeysOptions.Limit))
}
if listKeysOptions.Offset != nil {
values.Set("offset", fmt.Sprint(*listKeysOptions.Offset))
}
if listKeysOptions.State != nil {
var states []string
for _, i := range listKeysOptions.State {
states = append(states, strconv.Itoa(int(i)))
}
values.Set("state", strings.Join(states, ","))
}
if listKeysOptions.Extractable != nil {
values.Set("extractable", fmt.Sprint(*listKeysOptions.Extractable))
}
req.URL.RawQuery = values.Encode()
}
keys := Keys{}
_, err = c.do(ctx, req, &keys)
if err != nil {
return nil, err
}
return &keys, nil
}
// GetKey retrieves a key by ID or alias name. // GetKey retrieves a key by ID or alias name.
// For more information on Key Alias please refer to the link below // For more information on Key Alias please refer to the link below
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-retrieve-key // https://cloud.ibm.com/docs/key-protect?topic=key-protect-retrieve-key
@ -245,10 +352,10 @@ func (c *Client) GetKeyMetadata(ctx context.Context, idOrAlias string) (*Key, er
return c.getKey(ctx, idOrAlias, "keys/%s/metadata") return c.getKey(ctx, idOrAlias, "keys/%s/metadata")
} }
func (c *Client) getKey(ctx context.Context, id string, path string) (*Key, error) { func (c *Client) getKey(ctx context.Context, idOrAlias string, path string) (*Key, error) {
keys := Keys{} keys := Keys{}
req, err := c.newRequest("GET", fmt.Sprintf(path, id), nil) req, err := c.newRequest("GET", fmt.Sprintf(path, idOrAlias), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -267,10 +374,51 @@ type ForceOpt struct {
Force bool Force bool
} }
// DeleteKey deletes a key resource by specifying the ID of the key. // ListKeyVersionsOptions struct to add the query parameters for the ListKeyVersions function
func (c *Client) DeleteKey(ctx context.Context, id string, prefer PreferReturn, callOpts ...CallOpt) (*Key, error) { type ListKeyVersionsOptions struct {
Limit *uint32
Offset *uint32
TotalCount *bool
}
req, err := c.newRequest("DELETE", fmt.Sprintf("keys/%s", id), nil) // ListKeyVersions gets all the versions of the key resource by specifying ID of the key and/or optional parameters
// https://cloud.ibm.com/apidocs/key-protect#getkeyversions
func (c *Client) ListKeyVersions(ctx context.Context, idOrAlias string, listKeyVersionsOptions *ListKeyVersionsOptions) (*KeyVersions, error) {
keyVersion := KeyVersions{}
// forming the request
req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/versions", idOrAlias), nil)
if err != nil {
return nil, err
}
// extracting the query parameters and encoding the same in the request url
if listKeyVersionsOptions != nil {
values := req.URL.Query()
if listKeyVersionsOptions.Limit != nil {
values.Set("limit", fmt.Sprint(*listKeyVersionsOptions.Limit))
}
if listKeyVersionsOptions.Offset != nil {
values.Set("offset", fmt.Sprint(*listKeyVersionsOptions.Offset))
}
if listKeyVersionsOptions.TotalCount != nil {
values.Set("totalCount", fmt.Sprint(*listKeyVersionsOptions.TotalCount))
}
req.URL.RawQuery = values.Encode()
}
//making a request
_, err = c.do(ctx, req, &keyVersion)
if err != nil {
return nil, err
}
return &keyVersion, nil
}
// DeleteKey deletes a key resource by specifying the ID of the key.
func (c *Client) DeleteKey(ctx context.Context, idOrAlias string, prefer PreferReturn, callOpts ...CallOpt) (*Key, error) {
req, err := c.newRequest("DELETE", fmt.Sprintf("keys/%s", idOrAlias), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -301,12 +449,37 @@ func (c *Client) DeleteKey(ctx context.Context, id string, prefer PreferReturn,
return nil, nil return nil, nil
} }
// Purge key method shreds all the metadata and registrations associated with a key that has been
// deleted. The purge operation is allowed to be performed on a key from 4 hours after its deletion
// and its action is irreversible.
// For more information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-delete-keys#delete-keys-key-purge
func (c *Client) PurgeKey(ctx context.Context, idOrAlias string, prefer PreferReturn) (*Key, error) {
req, err := c.newRequest("DELETE", fmt.Sprintf("keys/%s/purge", idOrAlias), nil)
if err != nil {
return nil, err
}
req.Header.Set("Prefer", preferHeaders[prefer])
keys := Keys{}
_, err = c.do(ctx, req, &keys)
if err != nil {
return nil, err
}
if len(keys.Keys) > 0 {
return &keys.Keys[0], nil
}
return nil, nil
}
// RestoreKey method reverts a delete key status to active key // RestoreKey method reverts a delete key status to active key
// This method performs restore of any key from deleted state to active state. // This method performs restore of any key from deleted state to active state.
// For more information please refer to the link below: // For more information please refer to the link below:
// https://cloud.ibm.com/dowcs/key-protect?topic=key-protect-restore-keys // https://cloud.ibm.com/docs/key-protect?topic=key-protect-restore-keys
func (c *Client) RestoreKey(ctx context.Context, id string) (*Key, error) { func (c *Client) RestoreKey(ctx context.Context, idOrAlias string) (*Key, error) {
req, err := c.newRequest("POST", fmt.Sprintf("keys/%s/restore", id), nil) req, err := c.newRequest("POST", fmt.Sprintf("keys/%s/restore", idOrAlias), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -322,17 +495,17 @@ func (c *Client) RestoreKey(ctx context.Context, id string) (*Key, error) {
} }
// Wrap calls the wrap action with the given plain text. // Wrap calls the wrap action with the given plain text.
func (c *Client) Wrap(ctx context.Context, id string, plainText []byte, additionalAuthData *[]string) ([]byte, error) { func (c *Client) Wrap(ctx context.Context, idOrAlias string, plainText []byte, additionalAuthData *[]string) ([]byte, error) {
_, ct, err := c.wrap(ctx, id, plainText, additionalAuthData) _, ct, err := c.wrap(ctx, idOrAlias, plainText, additionalAuthData)
return ct, err return ct, err
} }
// WrapCreateDEK calls the wrap action without plain text. // WrapCreateDEK calls the wrap action without plain text.
func (c *Client) WrapCreateDEK(ctx context.Context, id string, additionalAuthData *[]string) ([]byte, []byte, error) { func (c *Client) WrapCreateDEK(ctx context.Context, idOrAlias string, additionalAuthData *[]string) ([]byte, []byte, error) {
return c.wrap(ctx, id, nil, additionalAuthData) return c.wrap(ctx, idOrAlias, nil, additionalAuthData)
} }
func (c *Client) wrap(ctx context.Context, id string, plainText []byte, additionalAuthData *[]string) ([]byte, []byte, error) { func (c *Client) wrap(ctx context.Context, idOrAlias string, plainText []byte, additionalAuthData *[]string) ([]byte, []byte, error) {
keysActionReq := &KeysActionRequest{} keysActionReq := &KeysActionRequest{}
if plainText != nil { if plainText != nil {
@ -347,7 +520,7 @@ func (c *Client) wrap(ctx context.Context, id string, plainText []byte, addition
keysActionReq.AAD = *additionalAuthData keysActionReq.AAD = *additionalAuthData
} }
keysAction, err := c.doKeysAction(ctx, id, "wrap", keysActionReq) keysAction, err := c.doKeysAction(ctx, idOrAlias, "wrap", keysActionReq)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -359,8 +532,8 @@ func (c *Client) wrap(ctx context.Context, id string, plainText []byte, addition
} }
// Unwrap is deprecated since it returns only plaintext and doesn't know how to handle rotation. // Unwrap is deprecated since it returns only plaintext and doesn't know how to handle rotation.
func (c *Client) Unwrap(ctx context.Context, id string, cipherText []byte, additionalAuthData *[]string) ([]byte, error) { func (c *Client) Unwrap(ctx context.Context, idOrAlias string, cipherText []byte, additionalAuthData *[]string) ([]byte, error) {
plainText, _, err := c.UnwrapV2(ctx, id, cipherText, additionalAuthData) plainText, _, err := c.UnwrapV2(ctx, idOrAlias, cipherText, additionalAuthData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -368,7 +541,7 @@ func (c *Client) Unwrap(ctx context.Context, id string, cipherText []byte, addit
} }
// Unwrap with rotation support. // Unwrap with rotation support.
func (c *Client) UnwrapV2(ctx context.Context, id string, cipherText []byte, additionalAuthData *[]string) ([]byte, []byte, error) { func (c *Client) UnwrapV2(ctx context.Context, idOrAlias string, cipherText []byte, additionalAuthData *[]string) ([]byte, []byte, error) {
keysAction := &KeysActionRequest{ keysAction := &KeysActionRequest{
CipherText: string(cipherText), CipherText: string(cipherText),
@ -378,7 +551,7 @@ func (c *Client) UnwrapV2(ctx context.Context, id string, cipherText []byte, add
keysAction.AAD = *additionalAuthData keysAction.AAD = *additionalAuthData
} }
respAction, err := c.doKeysAction(ctx, id, "unwrap", keysAction) respAction, err := c.doKeysAction(ctx, idOrAlias, "unwrap", keysAction)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -390,13 +563,13 @@ func (c *Client) UnwrapV2(ctx context.Context, id string, cipherText []byte, add
} }
// Rotate rotates a CRK. // Rotate rotates a CRK.
func (c *Client) Rotate(ctx context.Context, id, payload string) error { func (c *Client) Rotate(ctx context.Context, idOrAlias, payload string) error {
actionReq := &KeysActionRequest{ actionReq := &KeysActionRequest{
Payload: payload, Payload: payload,
} }
_, err := c.doKeysAction(ctx, id, "rotate", actionReq) _, err := c.doKeysAction(ctx, idOrAlias, "rotate", actionReq)
if err != nil { if err != nil {
return err return err
} }
@ -404,12 +577,78 @@ func (c *Client) Rotate(ctx context.Context, id, payload string) error {
return nil return nil
} }
type KeyPayload struct {
payload string
encryptedNonce string
iv string
encryptionAlgorithm string
}
func NewKeyPayload(payload, encryptedNonce, iv string) KeyPayload {
kp := KeyPayload{
payload: payload,
encryptedNonce: encryptedNonce,
iv: iv,
}
return kp
}
// EncryptWithRSA256 sets the encryption algorithm for key create to RSAES_OAEP_SHA_256
// This is the default algorithm for key creation under Key Protect service
func (kp KeyPayload) WithRSA256() KeyPayload {
kp.encryptionAlgorithm = "RSAES_OAEP_SHA_256"
return kp
}
// EncryptWithRSA1 sets the encryption algorithm for key create to RSAES_OAEP_SHA_1
// This algorithm is only supported by the Hyper Protect(HPCS) service
func (kp KeyPayload) WithRSA1() KeyPayload {
kp.encryptionAlgorithm = "RSAES_OAEP_SHA_1"
return kp
}
// RotateV2 methods supports rotation of a root key with or without payload and also rotate a
// securely imported root key.
func (c *Client) RotateV2(ctx context.Context, idOrAlias string, new_key *KeyPayload) error {
var actionReq *KeysActionRequest
if new_key != nil {
actionReq = &KeysActionRequest{
Payload: new_key.payload,
EncryptedNonce: new_key.encryptedNonce,
IV: new_key.iv,
EncryptionAlgorithm: new_key.encryptionAlgorithm,
}
}
_, err := c.doKeysAction(ctx, idOrAlias, "rotate", actionReq)
if err != nil {
return err
}
return nil
}
// SyncAssociatedResources method executes the sync request which verifies and updates
// the resources associated with the key.
// For more information please refer to the link below
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-sync-associated-resources
func (c *Client) SyncAssociatedResources(ctx context.Context, idOrAlias string) error {
req, err := c.newRequest("POST", fmt.Sprintf("keys/%s/actions/sync", idOrAlias), nil)
if err != nil {
return err
}
_, err = c.do(ctx, req, nil)
return err
}
// Disable a key. The key will not be deleted but it will not be active // Disable a key. The key will not be deleted but it will not be active
// and key operations cannot be performed on a disabled key. // and key operations cannot be performed on a disabled key.
// For more information can refer to the Key Protect docs in the link below: // For more information can refer to the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-disable-keys // https://cloud.ibm.com/docs/key-protect?topic=key-protect-disable-keys
func (c *Client) DisableKey(ctx context.Context, id string) error { func (c *Client) DisableKey(ctx context.Context, idOrAlias string) error {
_, err := c.doKeysAction(ctx, id, "disable", nil) _, err := c.doKeysAction(ctx, idOrAlias, "disable", nil)
return err return err
} }
@ -418,8 +657,8 @@ func (c *Client) DisableKey(ctx context.Context, id string) error {
// Note: This does not recover Deleted keys. // Note: This does not recover Deleted keys.
// For more information can refer to the Key Protect docs in the link below: // For more information can refer to the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-disable-keys#enable-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-disable-keys#enable-api
func (c *Client) EnableKey(ctx context.Context, id string) error { func (c *Client) EnableKey(ctx context.Context, idOrAlias string) error {
_, err := c.doKeysAction(ctx, id, "enable", nil) _, err := c.doKeysAction(ctx, idOrAlias, "enable", nil)
return err return err
} }
@ -427,8 +666,8 @@ func (c *Client) EnableKey(ctx context.Context, id string) error {
// After the key is set to deletion it can be deleted by another user who has Manager access. // After the key is set to deletion it can be deleted by another user who has Manager access.
// For more information refer to the Key Protect docs in the link below: // For more information refer to the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-delete-dual-auth-keys#set-key-deletion-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-delete-dual-auth-keys#set-key-deletion-api
func (c *Client) InitiateDualAuthDelete(ctx context.Context, id string) error { func (c *Client) InitiateDualAuthDelete(ctx context.Context, idOrAlias string) error {
_, err := c.doKeysAction(ctx, id, "setKeyForDeletion", nil) _, err := c.doKeysAction(ctx, idOrAlias, "setKeyForDeletion", nil)
return err return err
} }
@ -436,25 +675,20 @@ func (c *Client) InitiateDualAuthDelete(ctx context.Context, id string) error {
// be prevented from getting deleted by unsetting the key for deletion. // be prevented from getting deleted by unsetting the key for deletion.
// For more information refer to the Key Protect docs in the link below: // For more information refer to the Key Protect docs in the link below:
//https://cloud.ibm.com/docs/key-protect?topic=key-protect-delete-dual-auth-keys#unset-key-deletion-api //https://cloud.ibm.com/docs/key-protect?topic=key-protect-delete-dual-auth-keys#unset-key-deletion-api
func (c *Client) CancelDualAuthDelete(ctx context.Context, id string) error { func (c *Client) CancelDualAuthDelete(ctx context.Context, idOrAlias string) error {
_, err := c.doKeysAction(ctx, id, "unsetKeyForDeletion", nil) _, err := c.doKeysAction(ctx, idOrAlias, "unsetKeyForDeletion", nil)
return err return err
} }
// doKeysAction calls the KP Client to perform an action on a key. // doKeysAction calls the KP Client to perform an action on a key.
func (c *Client) doKeysAction(ctx context.Context, id string, action string, keysActionReq *KeysActionRequest) (*KeysActionRequest, error) { func (c *Client) doKeysAction(ctx context.Context, idOrAlias string, action string, keysActionReq *KeysActionRequest) (*KeysActionRequest, error) {
keyActionRsp := KeysActionRequest{} keyActionRsp := KeysActionRequest{}
v := url.Values{} req, err := c.newRequest("POST", fmt.Sprintf("keys/%s/actions/%s", idOrAlias, action), keysActionReq)
v.Set("action", action)
req, err := c.newRequest("POST", fmt.Sprintf("keys/%s", id), keysActionReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.URL.RawQuery = v.Encode()
_, err = c.do(ctx, req, &keyActionRsp) _, err = c.do(ctx, req, &keyActionRsp)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -59,15 +59,15 @@ type Policies struct {
Policies []Policy `json:"resources"` Policies []Policy `json:"resources"`
} }
// GetPolicy retrieves a policy by Key ID. This function is // GetPolicy retrieves a policy by Key ID or alias. This function is
// deprecated, as it only returns one policy and does not let you // deprecated, as it only returns one policy and does not let you
// select which policy set it will return. It is kept for backward // select which policy set it will return. It is kept for backward
// compatibility on keys with only one rotation policy. Please update // compatibility on keys with only one rotation policy. Please update
// to use the new GetPolicies or Get<type>Policy functions. // to use the new GetPolicies or Get<type>Policy functions.
func (c *Client) GetPolicy(ctx context.Context, id string) (*Policy, error) { func (c *Client) GetPolicy(ctx context.Context, idOrAlias string) (*Policy, error) {
policyresponse := Policies{} policyresponse := Policies{}
req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", id), nil) req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", idOrAlias), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -84,7 +84,7 @@ func (c *Client) GetPolicy(ctx context.Context, id string) (*Policy, error) {
// the rotation interval needed. This function is deprecated as it will only // the rotation interval needed. This function is deprecated as it will only
// let you set key rotation policies. To set dual auth and other newer policies // let you set key rotation policies. To set dual auth and other newer policies
// on a key, please use the new SetPolicies of Set<type>Policy functions. // on a key, please use the new SetPolicies of Set<type>Policy functions.
func (c *Client) SetPolicy(ctx context.Context, id string, prefer PreferReturn, rotationInterval int) (*Policy, error) { func (c *Client) SetPolicy(ctx context.Context, idOrAlias string, prefer PreferReturn, rotationInterval int) (*Policy, error) {
policy := Policy{ policy := Policy{
Type: policyType, Type: policyType,
@ -103,7 +103,7 @@ func (c *Client) SetPolicy(ctx context.Context, id string, prefer PreferReturn,
policyresponse := Policies{} policyresponse := Policies{}
req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", id), &policyRequest) req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", idOrAlias), &policyRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -118,11 +118,11 @@ func (c *Client) SetPolicy(ctx context.Context, id string, prefer PreferReturn,
return &policyresponse.Policies[0], nil return &policyresponse.Policies[0], nil
} }
// GetPolicies retrieves all policies details associated with a Key ID. // GetPolicies retrieves all policies details associated with a Key ID or alias.
func (c *Client) GetPolicies(ctx context.Context, id string) ([]Policy, error) { func (c *Client) GetPolicies(ctx context.Context, idOrAlias string) ([]Policy, error) {
policyresponse := Policies{} policyresponse := Policies{}
req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", id), nil) req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", idOrAlias), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -155,10 +155,10 @@ func (c *Client) getPolicy(ctx context.Context, id, policyType string, policyres
// GetRotationPolivy method retrieves rotation policy details of a key // GetRotationPolivy method retrieves rotation policy details of a key
// For more information can refet the Key Protect docs in the link below: // For more information can refet the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-rotation-policy#view-rotation-policy-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-rotation-policy#view-rotation-policy-api
func (c *Client) GetRotationPolicy(ctx context.Context, id string) (*Policy, error) { func (c *Client) GetRotationPolicy(ctx context.Context, idOrAlias string) (*Policy, error) {
policyresponse := Policies{} policyresponse := Policies{}
err := c.getPolicy(ctx, id, RotationPolicy, &policyresponse) err := c.getPolicy(ctx, idOrAlias, RotationPolicy, &policyresponse)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -173,10 +173,10 @@ func (c *Client) GetRotationPolicy(ctx context.Context, id string) (*Policy, err
// GetDualAuthDeletePolicy method retrieves dual auth delete policy details of a key // GetDualAuthDeletePolicy method retrieves dual auth delete policy details of a key
// For more information can refer the Key Protect docs in the link below: // For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-dual-auth-key-policy#view-dual-auth-key-policy-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-dual-auth-key-policy#view-dual-auth-key-policy-api
func (c *Client) GetDualAuthDeletePolicy(ctx context.Context, id string) (*Policy, error) { func (c *Client) GetDualAuthDeletePolicy(ctx context.Context, idOrAlias string) (*Policy, error) {
policyresponse := Policies{} policyresponse := Policies{}
err := c.getPolicy(ctx, id, DualAuthDelete, &policyresponse) err := c.getPolicy(ctx, idOrAlias, DualAuthDelete, &policyresponse)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -188,10 +188,10 @@ func (c *Client) GetDualAuthDeletePolicy(ctx context.Context, id string) (*Polic
return &policyresponse.Policies[0], nil return &policyresponse.Policies[0], nil
} }
func (c *Client) setPolicy(ctx context.Context, id, policyType string, policyRequest Policies) (*Policies, error) { func (c *Client) setPolicy(ctx context.Context, idOrAlias, policyType string, policyRequest Policies) (*Policies, error) {
policyresponse := Policies{} policyresponse := Policies{}
req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", id), &policyRequest) req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", idOrAlias), &policyRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -207,10 +207,10 @@ func (c *Client) setPolicy(ctx context.Context, id, policyType string, policyReq
return &policyresponse, nil return &policyresponse, nil
} }
// SetRotationPolicy updates the rotation policy associated with a key by specifying key ID and rotation interval. // SetRotationPolicy updates the rotation policy associated with a key by specifying key ID or alias and rotation interval.
// For more information can refer the Key Protect docs in the link below: // For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-rotation-policy#update-rotation-policy-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-rotation-policy#update-rotation-policy-api
func (c *Client) SetRotationPolicy(ctx context.Context, id string, rotationInterval int) (*Policy, error) { func (c *Client) SetRotationPolicy(ctx context.Context, idOrAlias string, rotationInterval int) (*Policy, error) {
policy := Policy{ policy := Policy{
Type: policyType, Type: policyType,
Rotation: &Rotation{ Rotation: &Rotation{
@ -226,7 +226,7 @@ func (c *Client) SetRotationPolicy(ctx context.Context, id string, rotationInter
Policies: []Policy{policy}, Policies: []Policy{policy},
} }
policyresponse, err := c.setPolicy(ctx, id, RotationPolicy, policyRequest) policyresponse, err := c.setPolicy(ctx, idOrAlias, RotationPolicy, policyRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -238,10 +238,10 @@ func (c *Client) SetRotationPolicy(ctx context.Context, id string, rotationInter
return &policyresponse.Policies[0], nil return &policyresponse.Policies[0], nil
} }
// SetDualAuthDeletePolicy updates the dual auth delete policy by passing the key ID and enable detail // SetDualAuthDeletePolicy updates the dual auth delete policy by passing the key ID or alias and enable detail
// For more information can refer the Key Protect docs in the link below: // For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-dual-auth-key-policy#create-dual-auth-key-policy-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-dual-auth-key-policy#create-dual-auth-key-policy-api
func (c *Client) SetDualAuthDeletePolicy(ctx context.Context, id string, enabled bool) (*Policy, error) { func (c *Client) SetDualAuthDeletePolicy(ctx context.Context, idOrAlias string, enabled bool) (*Policy, error) {
policy := Policy{ policy := Policy{
Type: policyType, Type: policyType,
DualAuth: &DualAuth{ DualAuth: &DualAuth{
@ -257,7 +257,7 @@ func (c *Client) SetDualAuthDeletePolicy(ctx context.Context, id string, enabled
Policies: []Policy{policy}, Policies: []Policy{policy},
} }
policyresponse, err := c.setPolicy(ctx, id, DualAuthDelete, policyRequest) policyresponse, err := c.setPolicy(ctx, idOrAlias, DualAuthDelete, policyRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -273,7 +273,7 @@ func (c *Client) SetDualAuthDeletePolicy(ctx context.Context, id string, enabled
// To set rotation policy for the key pass the setRotationPolicy parameter as true and set the rotationInterval detail. // To set rotation policy for the key pass the setRotationPolicy parameter as true and set the rotationInterval detail.
// To set dual auth delete policy for the key pass the setDualAuthDeletePolicy parameter as true and set the dualAuthEnable detail. // To set dual auth delete policy for the key pass the setDualAuthDeletePolicy parameter as true and set the dualAuthEnable detail.
// Both the policies can be set or either of the policies can be set. // Both the policies can be set or either of the policies can be set.
func (c *Client) SetPolicies(ctx context.Context, id string, setRotationPolicy bool, rotationInterval int, setDualAuthDeletePolicy, dualAuthEnable bool) ([]Policy, error) { func (c *Client) SetPolicies(ctx context.Context, idOrAlias string, setRotationPolicy bool, rotationInterval int, setDualAuthDeletePolicy, dualAuthEnable bool) ([]Policy, error) {
policies := []Policy{} policies := []Policy{}
if setRotationPolicy { if setRotationPolicy {
rotationPolicy := Policy{ rotationPolicy := Policy{
@ -304,7 +304,7 @@ func (c *Client) SetPolicies(ctx context.Context, id string, setRotationPolicy b
policyresponse := Policies{} policyresponse := Policies{}
req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", id), &policyRequest) req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", idOrAlias), &policyRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -26,8 +26,8 @@ var (
// NewMD5 and NewSHA1. // NewMD5 and NewSHA1.
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
h.Reset() h.Reset()
h.Write(space[:]) h.Write(space[:]) //nolint:errcheck
h.Write(data) h.Write(data) //nolint:errcheck
s := h.Sum(nil) s := h.Sum(nil)
var uuid UUID var uuid UUID
copy(uuid[:], s) copy(uuid[:], s)

118
vendor/github.com/google/uuid/null.go generated vendored Normal file
View File

@ -0,0 +1,118 @@
// Copyright 2021 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"bytes"
"database/sql/driver"
"encoding/json"
"fmt"
)
var jsonNull = []byte("null")
// NullUUID represents a UUID that may be null.
// NullUUID implements the SQL driver.Scanner interface so
// it can be used as a scan destination:
//
// var u uuid.NullUUID
// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u)
// ...
// if u.Valid {
// // use u.UUID
// } else {
// // NULL value
// }
//
type NullUUID struct {
UUID UUID
Valid bool // Valid is true if UUID is not NULL
}
// Scan implements the SQL driver.Scanner interface.
func (nu *NullUUID) Scan(value interface{}) error {
if value == nil {
nu.UUID, nu.Valid = Nil, false
return nil
}
err := nu.UUID.Scan(value)
if err != nil {
nu.Valid = false
return err
}
nu.Valid = true
return nil
}
// Value implements the driver Valuer interface.
func (nu NullUUID) Value() (driver.Value, error) {
if !nu.Valid {
return nil, nil
}
// Delegate to UUID Value function
return nu.UUID.Value()
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (nu NullUUID) MarshalBinary() ([]byte, error) {
if nu.Valid {
return nu.UUID[:], nil
}
return []byte(nil), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (nu *NullUUID) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
}
copy(nu.UUID[:], data)
nu.Valid = true
return nil
}
// MarshalText implements encoding.TextMarshaler.
func (nu NullUUID) MarshalText() ([]byte, error) {
if nu.Valid {
return nu.UUID.MarshalText()
}
return jsonNull, nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (nu *NullUUID) UnmarshalText(data []byte) error {
id, err := ParseBytes(data)
if err != nil {
nu.Valid = false
return err
}
nu.UUID = id
nu.Valid = true
return nil
}
// MarshalJSON implements json.Marshaler.
func (nu NullUUID) MarshalJSON() ([]byte, error) {
if nu.Valid {
return json.Marshal(nu.UUID)
}
return jsonNull, nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (nu *NullUUID) UnmarshalJSON(data []byte) error {
if bytes.Equal(data, jsonNull) {
*nu = NullUUID{}
return nil // valid null UUID
}
err := json.Unmarshal(data, &nu.UUID)
nu.Valid = err == nil
return err
}

View File

@ -9,7 +9,7 @@ import (
"fmt" "fmt"
) )
// Scan implements sql.Scanner so UUIDs can be read from databases transparently // Scan implements sql.Scanner so UUIDs can be read from databases transparently.
// Currently, database types that map to string and []byte are supported. Please // Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types. // consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error { func (uuid *UUID) Scan(src interface{}) error {

View File

@ -12,6 +12,7 @@ import (
"fmt" "fmt"
"io" "io"
"strings" "strings"
"sync"
) )
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
@ -33,7 +34,27 @@ const (
Future // Reserved for future definition. Future // Reserved for future definition.
) )
var rander = rand.Reader // random function const randPoolSize = 16 * 16
var (
rander = rand.Reader // random function
poolEnabled = false
poolMu sync.Mutex
poolPos = randPoolSize // protected with poolMu
pool [randPoolSize]byte // protected with poolMu
)
type invalidLengthError struct{ len int }
func (err invalidLengthError) Error() string {
return fmt.Sprintf("invalid UUID length: %d", err.len)
}
// IsInvalidLengthError is matcher function for custom error invalidLengthError
func IsInvalidLengthError(err error) bool {
_, ok := err.(invalidLengthError)
return ok
}
// Parse decodes s into a UUID or returns an error. Both the standard UUID // Parse decodes s into a UUID or returns an error. Both the standard UUID
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and // forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
@ -68,7 +89,7 @@ func Parse(s string) (UUID, error) {
} }
return uuid, nil return uuid, nil
default: default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) return uuid, invalidLengthError{len(s)}
} }
// s is now at least 36 bytes long // s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
@ -112,7 +133,7 @@ func ParseBytes(b []byte) (UUID, error) {
} }
return uuid, nil return uuid, nil
default: default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(b)) return uuid, invalidLengthError{len(b)}
} }
// s is now at least 36 bytes long // s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
@ -243,3 +264,31 @@ func SetRand(r io.Reader) {
} }
rander = r rander = r
} }
// EnableRandPool enables internal randomness pool used for Random
// (Version 4) UUID generation. The pool contains random bytes read from
// the random number generator on demand in batches. Enabling the pool
// may improve the UUID generation throughput significantly.
//
// Since the pool is stored on the Go heap, this feature may be a bad fit
// for security sensitive applications.
//
// Both EnableRandPool and DisableRandPool are not thread-safe and should
// only be called when there is no possibility that New or any other
// UUID Version 4 generation function will be called concurrently.
func EnableRandPool() {
poolEnabled = true
}
// DisableRandPool disables the randomness pool if it was previously
// enabled with EnableRandPool.
//
// Both EnableRandPool and DisableRandPool are not thread-safe and should
// only be called when there is no possibility that New or any other
// UUID Version 4 generation function will be called concurrently.
func DisableRandPool() {
poolEnabled = false
defer poolMu.Unlock()
poolMu.Lock()
poolPos = randPoolSize
}

View File

@ -14,11 +14,21 @@ func New() UUID {
return Must(NewRandom()) return Must(NewRandom())
} }
// NewString creates a new random UUID and returns it as a string or panics.
// NewString is equivalent to the expression
//
// uuid.New().String()
func NewString() string {
return Must(NewRandom()).String()
}
// NewRandom returns a Random (Version 4) UUID. // NewRandom returns a Random (Version 4) UUID.
// //
// The strength of the UUIDs is based on the strength of the crypto/rand // The strength of the UUIDs is based on the strength of the crypto/rand
// package. // package.
// //
// Uses the randomness pool if it was enabled with EnableRandPool.
//
// A note about uniqueness derived from the UUID Wikipedia entry: // A note about uniqueness derived from the UUID Wikipedia entry:
// //
// Randomly generated UUIDs have 122 random bits. One's annual risk of being // Randomly generated UUIDs have 122 random bits. One's annual risk of being
@ -27,7 +37,10 @@ func New() UUID {
// equivalent to the odds of creating a few tens of trillions of UUIDs in a // equivalent to the odds of creating a few tens of trillions of UUIDs in a
// year and having one duplicate. // year and having one duplicate.
func NewRandom() (UUID, error) { func NewRandom() (UUID, error) {
return NewRandomFromReader(rander) if !poolEnabled {
return NewRandomFromReader(rander)
}
return newRandomFromPool()
} }
// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. // NewRandomFromReader returns a UUID based on bytes read from a given io.Reader.
@ -41,3 +54,23 @@ func NewRandomFromReader(r io.Reader) (UUID, error) {
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil return uuid, nil
} }
func newRandomFromPool() (UUID, error) {
var uuid UUID
poolMu.Lock()
if poolPos == randPoolSize {
_, err := io.ReadFull(rander, pool[:])
if err != nil {
poolMu.Unlock()
return Nil, err
}
poolPos = 0
}
copy(uuid[:], pool[poolPos:(poolPos+16)])
poolPos += 16
poolMu.Unlock()
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}

View File

@ -1,12 +0,0 @@
sudo: false
language: go
go:
- 1.12.4
branches:
only:
- master
script: make updatedeps test

View File

@ -26,6 +26,7 @@ fails so that the full request can be attempted again. See the
details. details.
Version 0.6.0 and before are compatible with Go prior to 1.12. From 0.6.1 onward, Go 1.12+ is required. Version 0.6.0 and before are compatible with Go prior to 1.12. From 0.6.1 onward, Go 1.12+ is required.
From 0.6.7 onward, Go 1.13+ is required.
Example Use Example Use
=========== ===========

View File

@ -35,11 +35,12 @@ import (
"net/url" "net/url"
"os" "os"
"regexp" "regexp"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/hashicorp/go-cleanhttp" cleanhttp "github.com/hashicorp/go-cleanhttp"
) )
var ( var (
@ -276,12 +277,16 @@ type Logger interface {
Printf(string, ...interface{}) Printf(string, ...interface{})
} }
// LeveledLogger interface implements the basic methods that a logger library needs // LeveledLogger is an interface that can be implemented by any logger or a
// logger wrapper to provide leveled logging. The methods accept a message
// string and a variadic number of key-value pairs. For log.Printf style
// formatting where message string contains a format specifier, use Logger
// interface.
type LeveledLogger interface { type LeveledLogger interface {
Error(string, ...interface{}) Error(msg string, keysAndValues ...interface{})
Info(string, ...interface{}) Info(msg string, keysAndValues ...interface{})
Debug(string, ...interface{}) Debug(msg string, keysAndValues ...interface{})
Warn(string, ...interface{}) Warn(msg string, keysAndValues ...interface{})
} }
// hookLogger adapts an LeveledLogger to Logger for use by the existing hook functions // hookLogger adapts an LeveledLogger to Logger for use by the existing hook functions
@ -357,6 +362,7 @@ type Client struct {
ErrorHandler ErrorHandler ErrorHandler ErrorHandler
loggerInit sync.Once loggerInit sync.Once
clientInit sync.Once
} }
// NewClient creates a new Client with default settings. // NewClient creates a new Client with default settings.
@ -398,21 +404,39 @@ func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bo
return false, ctx.Err() return false, ctx.Err()
} }
// don't propagate other errors
shouldRetry, _ := baseRetryPolicy(resp, err)
return shouldRetry, nil
}
// ErrorPropagatedRetryPolicy is the same as DefaultRetryPolicy, except it
// propagates errors back instead of returning nil. This allows you to inspect
// why it decided to retry or not.
func ErrorPropagatedRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) {
// do not retry on context.Canceled or context.DeadlineExceeded
if ctx.Err() != nil {
return false, ctx.Err()
}
return baseRetryPolicy(resp, err)
}
func baseRetryPolicy(resp *http.Response, err error) (bool, error) {
if err != nil { if err != nil {
if v, ok := err.(*url.Error); ok { if v, ok := err.(*url.Error); ok {
// Don't retry if the error was due to too many redirects. // Don't retry if the error was due to too many redirects.
if redirectsErrorRe.MatchString(v.Error()) { if redirectsErrorRe.MatchString(v.Error()) {
return false, nil return false, v
} }
// Don't retry if the error was due to an invalid protocol scheme. // Don't retry if the error was due to an invalid protocol scheme.
if schemeErrorRe.MatchString(v.Error()) { if schemeErrorRe.MatchString(v.Error()) {
return false, nil return false, v
} }
// Don't retry if the error was due to TLS cert verification failure. // Don't retry if the error was due to TLS cert verification failure.
if _, ok := v.Err.(x509.UnknownAuthorityError); ok { if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
return false, nil return false, v
} }
} }
@ -420,12 +444,19 @@ func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bo
return true, nil return true, nil
} }
// 429 Too Many Requests is recoverable. Sometimes the server puts
// a Retry-After response header to indicate when the server is
// available to start processing request from client.
if resp.StatusCode == http.StatusTooManyRequests {
return true, nil
}
// Check the response code. We retry on 500-range responses to allow // Check the response code. We retry on 500-range responses to allow
// the server time to recover, as 500's are typically not permanent // the server time to recover, as 500's are typically not permanent
// errors and may relate to outages on the server side. This will catch // errors and may relate to outages on the server side. This will catch
// invalid response codes as well, like 0 and 999. // invalid response codes as well, like 0 and 999.
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) { if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) {
return true, nil return true, fmt.Errorf("unexpected HTTP status %s", resp.Status)
} }
return false, nil return false, nil
@ -434,7 +465,21 @@ func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bo
// DefaultBackoff provides a default callback for Client.Backoff which // DefaultBackoff provides a default callback for Client.Backoff which
// will perform exponential backoff based on the attempt number and limited // will perform exponential backoff based on the attempt number and limited
// by the provided minimum and maximum durations. // by the provided minimum and maximum durations.
//
// It also tries to parse Retry-After response header when a http.StatusTooManyRequests
// (HTTP Code 429) is found in the resp parameter. Hence it will return the number of
// seconds the server states it may be ready to process more requests from this client.
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
if resp != nil {
if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode == http.StatusServiceUnavailable {
if s, ok := resp.Header["Retry-After"]; ok {
if sleep, err := strconv.ParseInt(s[0], 10, 64); err == nil {
return time.Second * time.Duration(sleep)
}
}
}
}
mult := math.Pow(2, float64(attemptNum)) * float64(min) mult := math.Pow(2, float64(attemptNum)) * float64(min)
sleep := time.Duration(mult) sleep := time.Duration(mult)
if float64(sleep) != mult || sleep > max { if float64(sleep) != mult || sleep > max {
@ -490,25 +535,31 @@ func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Respo
// Do wraps calling an HTTP method with retries. // Do wraps calling an HTTP method with retries.
func (c *Client) Do(req *Request) (*http.Response, error) { func (c *Client) Do(req *Request) (*http.Response, error) {
if c.HTTPClient == nil { c.clientInit.Do(func() {
c.HTTPClient = cleanhttp.DefaultPooledClient() if c.HTTPClient == nil {
} c.HTTPClient = cleanhttp.DefaultPooledClient()
}
})
logger := c.logger() logger := c.logger()
if logger != nil { if logger != nil {
switch v := logger.(type) { switch v := logger.(type) {
case Logger:
v.Printf("[DEBUG] %s %s", req.Method, req.URL)
case LeveledLogger: case LeveledLogger:
v.Debug("performing request", "method", req.Method, "url", req.URL) v.Debug("performing request", "method", req.Method, "url", req.URL)
case Logger:
v.Printf("[DEBUG] %s %s", req.Method, req.URL)
} }
} }
var resp *http.Response var resp *http.Response
var err error var attempt int
var shouldRetry bool
var doErr, checkErr error
for i := 0; ; i++ { for i := 0; ; i++ {
attempt++
var code int // HTTP response code var code int // HTTP response code
// Always rewind the request body when non-nil. // Always rewind the request body when non-nil.
@ -527,30 +578,30 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
if c.RequestLogHook != nil { if c.RequestLogHook != nil {
switch v := logger.(type) { switch v := logger.(type) {
case Logger:
c.RequestLogHook(v, req.Request, i)
case LeveledLogger: case LeveledLogger:
c.RequestLogHook(hookLogger{v}, req.Request, i) c.RequestLogHook(hookLogger{v}, req.Request, i)
case Logger:
c.RequestLogHook(v, req.Request, i)
default: default:
c.RequestLogHook(nil, req.Request, i) c.RequestLogHook(nil, req.Request, i)
} }
} }
// Attempt the request // Attempt the request
resp, err = c.HTTPClient.Do(req.Request) resp, doErr = c.HTTPClient.Do(req.Request)
if resp != nil { if resp != nil {
code = resp.StatusCode code = resp.StatusCode
} }
// Check if we should continue with retries. // Check if we should continue with retries.
checkOK, checkErr := c.CheckRetry(req.Context(), resp, err) shouldRetry, checkErr = c.CheckRetry(req.Context(), resp, doErr)
if err != nil { if doErr != nil {
switch v := logger.(type) { switch v := logger.(type) {
case Logger:
v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err)
case LeveledLogger: case LeveledLogger:
v.Error("request failed", "error", err, "method", req.Method, "url", req.URL) v.Error("request failed", "error", doErr, "method", req.Method, "url", req.URL)
case Logger:
v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, doErr)
} }
} else { } else {
// Call this here to maintain the behavior of logging all requests, // Call this here to maintain the behavior of logging all requests,
@ -558,23 +609,18 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
if c.ResponseLogHook != nil { if c.ResponseLogHook != nil {
// Call the response logger function if provided. // Call the response logger function if provided.
switch v := logger.(type) { switch v := logger.(type) {
case Logger:
c.ResponseLogHook(v, resp)
case LeveledLogger: case LeveledLogger:
c.ResponseLogHook(hookLogger{v}, resp) c.ResponseLogHook(hookLogger{v}, resp)
case Logger:
c.ResponseLogHook(v, resp)
default: default:
c.ResponseLogHook(nil, resp) c.ResponseLogHook(nil, resp)
} }
} }
} }
// Now decide if we should continue. if !shouldRetry {
if !checkOK { break
if checkErr != nil {
err = checkErr
}
c.HTTPClient.CloseIdleConnections()
return resp, err
} }
// We do this before drainBody because there's no need for the I/O if // We do this before drainBody because there's no need for the I/O if
@ -585,7 +631,7 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
} }
// We're going to retry, consume any response to reuse the connection. // We're going to retry, consume any response to reuse the connection.
if err == nil && resp != nil { if doErr == nil {
c.drainBody(resp.Body) c.drainBody(resp.Body)
} }
@ -596,10 +642,10 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
} }
if logger != nil { if logger != nil {
switch v := logger.(type) { switch v := logger.(type) {
case Logger:
v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
case LeveledLogger: case LeveledLogger:
v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain) v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain)
case Logger:
v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
} }
} }
select { select {
@ -608,21 +654,44 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
return nil, req.Context().Err() return nil, req.Context().Err()
case <-time.After(wait): case <-time.After(wait):
} }
// Make shallow copy of http Request so that we can modify its body
// without racing against the closeBody call in persistConn.writeLoop.
httpreq := *req.Request
req.Request = &httpreq
}
// this is the closest we have to success criteria
if doErr == nil && checkErr == nil && !shouldRetry {
return resp, nil
}
defer c.HTTPClient.CloseIdleConnections()
err := doErr
if checkErr != nil {
err = checkErr
} }
if c.ErrorHandler != nil { if c.ErrorHandler != nil {
c.HTTPClient.CloseIdleConnections() return c.ErrorHandler(resp, err, attempt)
return c.ErrorHandler(resp, err, c.RetryMax+1)
} }
// By default, we close the response body and return an error without // By default, we close the response body and return an error without
// returning the response // returning the response
if resp != nil { if resp != nil {
resp.Body.Close() c.drainBody(resp.Body)
} }
c.HTTPClient.CloseIdleConnections()
return nil, fmt.Errorf("%s %s giving up after %d attempts", // this means CheckRetry thought the request was a failure, but didn't
req.Method, req.URL, c.RetryMax+1) // communicate why
if err == nil {
return nil, fmt.Errorf("%s %s giving up after %d attempt(s)",
req.Method, req.URL, attempt)
}
return nil, fmt.Errorf("%s %s giving up after %d attempt(s): %w",
req.Method, req.URL, attempt, err)
} }
// Try to read the response body so we can reuse this connection. // Try to read the response body so we can reuse this connection.
@ -632,10 +701,10 @@ func (c *Client) drainBody(body io.ReadCloser) {
if err != nil { if err != nil {
if c.logger() != nil { if c.logger() != nil {
switch v := c.logger().(type) { switch v := c.logger().(type) {
case Logger:
v.Printf("[ERR] error reading response body: %v", err)
case LeveledLogger: case LeveledLogger:
v.Error("error reading response body", "error", err) v.Error("error reading response body", "error", err)
case Logger:
v.Printf("[ERR] error reading response body: %v", err)
} }
} }
} }

View File

@ -1,7 +1,9 @@
package retryablehttp package retryablehttp
import ( import (
"errors"
"net/http" "net/http"
"net/url"
"sync" "sync"
) )
@ -39,5 +41,12 @@ func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
} }
// Execute the request. // Execute the request.
return rt.Client.Do(retryableReq) resp, err := rt.Client.Do(retryableReq)
// If we got an error returned by standard library's `Do` method, unwrap it
// otherwise we will wind up erroneously re-nesting the error.
if _, ok := err.(*url.Error); ok {
return resp, errors.Unwrap(err)
}
return resp, err
} }

8
vendor/modules.txt vendored
View File

@ -1,5 +1,5 @@
# github.com/IBM/keyprotect-go-client v0.7.0 # github.com/IBM/keyprotect-go-client v0.8.0
## explicit; go 1.12 ## explicit; go 1.15
github.com/IBM/keyprotect-go-client github.com/IBM/keyprotect-go-client
github.com/IBM/keyprotect-go-client/iam github.com/IBM/keyprotect-go-client/iam
# github.com/PuerkitoBio/purell v1.1.1 # github.com/PuerkitoBio/purell v1.1.1
@ -229,7 +229,7 @@ github.com/google/go-cmp/cmp/internal/value
# github.com/google/gofuzz v1.1.0 # github.com/google/gofuzz v1.1.0
## explicit; go 1.12 ## explicit; go 1.12
github.com/google/gofuzz github.com/google/gofuzz
# github.com/google/uuid v1.1.2 # github.com/google/uuid v1.3.0
## explicit ## explicit
github.com/google/uuid github.com/google/uuid
# github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 # github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
@ -262,7 +262,7 @@ github.com/hashicorp/go-multierror
## explicit; go 1.13 ## explicit; go 1.13
github.com/hashicorp/go-plugin github.com/hashicorp/go-plugin
github.com/hashicorp/go-plugin/internal/plugin github.com/hashicorp/go-plugin/internal/plugin
# github.com/hashicorp/go-retryablehttp v0.6.6 # github.com/hashicorp/go-retryablehttp v0.7.0
## explicit; go 1.13 ## explicit; go 1.13
github.com/hashicorp/go-retryablehttp github.com/hashicorp/go-retryablehttp
# github.com/hashicorp/go-rootcerts v1.0.2 # github.com/hashicorp/go-rootcerts v1.0.2