rebase: IBM key protect integration module dependency update

This commit adds the Key protect client SDK for the Key Protect
KMS integration to the Ceph CSI driver.

Signed-off-by: Humble Chirammal <hchiramm@redhat.com>
This commit is contained in:
Humble Chirammal 2021-12-20 17:49:02 +05:30 committed by mergify[bot]
parent 967076e4ba
commit 93e43d1a0f
18 changed files with 3025 additions and 0 deletions

1
go.mod
View File

@ -3,6 +3,7 @@ module github.com/ceph/ceph-csi
go 1.17
require (
github.com/IBM/keyprotect-go-client v0.7.0
github.com/aws/aws-sdk-go v1.42.7
github.com/ceph/ceph-csi/api v0.0.0-00010101000000-000000000000
github.com/ceph/go-ceph v0.12.0

4
go.sum
View File

@ -80,6 +80,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/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.7.0 h1:JstSHD14Lp6ihwQseyPuGcs1AjOBjAmcisP0dTBA6A0=
github.com/IBM/keyprotect-go-client v0.7.0/go.mod h1:SVr2ylV/fhSQPDiUjWirN9fsyWFCNNbt8GIT8hPJVjE=
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/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
@ -498,6 +500,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/consul-template v0.25.0/go.mod h1:/vUsrJvDuuQHcxEw0zik+YXTS7ZKWZjQeaQhshBmfH0=
@ -1626,6 +1629,7 @@ 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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

44
vendor/github.com/IBM/keyprotect-go-client/.gitignore generated vendored Normal file
View File

@ -0,0 +1,44 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
vendor
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
.DS_Store
cover.*
# Eclipse
.project
# Visual Studio Code
.vscode
/*.covtmp
**/coverage
**/coverage.htmlvendor
**/coverage.txt
**/coverage.html
**/cover.html
**/coverage.out
**/coverage.tmp
*.coverprofile

25
vendor/github.com/IBM/keyprotect-go-client/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,25 @@
language: go
dist: xenial
go:
- 1.14.x
- 1.15.x
env:
- GO111MODULE=on
before_script:
- GO111MODULE=off go get -u github.com/haya14busa/goverage
install:
- go build ./...
script:
- $GOPATH/bin/goverage -v -race -coverprofile=cover.out $(go list ./... | grep -v '/vendor|/scripts')
- go tool cover -func=cover.out
- go tool cover -html=cover.out -o=cover.html
# FIXME: these scripts don't exist in this repo
# after_success:
# - ./scripts/calculateCoverage.sh
# - ./scripts/publishCoverage.sh

View File

@ -0,0 +1,49 @@
# Contributing to keyprotect-go-client
`keyprotect-go-client` is open for code perusal and contributions. We welcome contributions in the form of feedback, bugs, or patches.
## Bugs and Feature Requests
If you find something that does not work as expected or would like to see a new feature added,
please open a [Github Issue](https://github.com/IBM/keyprotect-go-client/issues)
## Pull Requests
For your pull request to be merged, it must meet the criteria of a "correct patch", and also
be fully reviewed and approved by two Maintainer level contributors.
A correct patch is defined as the following:
- If the patch fixes a bug, it must be the simplest way to fix the issue
- Your patch must come with unit tests
- Unit tests (CI job) must pass
- New feature function should have integration tests as well
# Development
## Compiling the package
```sh
go build ./...
```
The client relies on go modules to pull in required dependencies at build time.
https://github.com/golang/go/wiki/Modules#how-to-use-modules
## Running the test cases
Using `go test`
```sh
go test -v -race ./...
```
The test cases are also runnable through `make`
```sh
make test
# or
make test-integration
```

201
vendor/github.com/IBM/keyprotect-go-client/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

8
vendor/github.com/IBM/keyprotect-go-client/Makefile generated vendored Normal file
View File

@ -0,0 +1,8 @@
.PHONY: test test-integration
test:
go test -v -race ./...
test-integration:
go test -v -tags=integration ./...

173
vendor/github.com/IBM/keyprotect-go-client/README.md generated vendored Normal file
View File

@ -0,0 +1,173 @@
# keyprotect-go-client
[![Build Status](https://travis-ci.com/IBM/keyprotect-go-client.svg?branch=master)](https://travis-ci.com/IBM/keyprotect-go-client)
[![GoDoc](https://godoc.org/github.com/keyprotect-go-client?status.svg)](https://godoc.org/github.com/IBM/keyprotect-go-client)
keyprotect-go-client is a Go client library for interacting with the IBM KeyProtect service.
* [Questions / Support](#questions--support)
* [Usage](#usage)
* [Migrating](#migrating)
* [Authentication](#authentication)
* [Finding Instance UUIDs](#finding-a-keyprotect-service-instances-uuid)
* [Examples](#examples)
* [Contributing](/CONTRIBUTING.md)
## Questions / Support
There are many channels for asking questions about KeyProtect and this client.
- Ask a question on Stackoverflow and tag it with `key-protect` and `ibm-cloud`
- Open a [Github Issue](https://github.com/IBM/keyprotect-go-client/issues)
- If you work at IBM and have access to the internal Slack, you can join the `#key-protect` channel and ask there.
## Usage
This client expects that you have an existing IBM Cloud Key Protect Service Instance. To get started, visit the [IBM KeyProtect Catalog Page](https://cloud.ibm.com/catalog/services/key-protect).
Build a client with `ClientConfig` and `New`, then use the client to do some operations.
```go
import "github.com/IBM/keyprotect-go-client"
// Use your IAM API Key and your KeyProtect Service Instance GUID/UUID to create a ClientConfig
cc := kp.ClientConfig{
BaseURL: kp.DefaultBaseURL,
APIKey: "......",
InstanceID: "1234abcd-906d-438a-8a68-deadbeef1a2b3",
}
// Build a new client from the config
client := kp.New(cc, kp.DefaultTransport())
// List keys in your KeyProtect instance
keys, err := client.GetKeys(context.Background(), 0, 0)
```
### Migrating
For users of the original `key-protect-client` that is now deprecated, this library is a drop in replacement. Updating the package reference to `github.com/IBM/keyprotect-go-client` should be the only change needed. If you are worried about new incompatible changes, version `v0.3.1` of `key-protect-client` is equivalent to version `v0.3.3` of `keyprotect-go-client`, so pinning `v0.3.3` of the new library should be sufficient to pull from the new repo with no new functional changes.
## Authentication
The KeyProtect client requires a valid [IAM API Key](https://cloud.ibm.com/docs/iam?topic=iam-userapikey#create_user_key) that is passed via the `APIKey` field in the `ClientConfig`. The client will call IAM to get an access token for that API key, caches the access token, and reuses that token on subsequent calls. If the access token is expired, the client will call IAM to get a new access token.
Alternatively, you may also inject your own tokens during runtime. When using your own tokens, it's the responsibilty of the caller to ensure the access token is valid and is not expired. You can specify the access token in either the `ClientConfig` structure or on the context (see below.)
To specify authorization token on the context:
```go
// Create a ClientConfig and Client like before, but without an APIKey
cc := kp.ClientConfig{
BaseURL: kp.DefaultBaseURL,
InstanceID: "1234abcd-906d-438a-8a68-deadbeef1a2b3",
}
client := kp.New(cc, kp.DefaultTransport())
// Use NewContextWithAuth to add your token into the context
ctx := context.Background()
ctx = kp.NewContextWithAuth(ctx, "Bearer ABCDEF123456....")
// List keys with our injected token via the context
keys, err := api.GetKeys(ctx, 0, 0)
```
For information on IAM API Keys and tokens please refer to the [IAM docs](https://cloud.ibm.com/docs/iam?topic=iam-manapikey)
## Finding a KeyProtect Service Instance's UUID
The client requires a valid UUID that identifies your KeyProtect Service Instance to be able to interact with your key data in the instance. An instance is somewhat like a folder or directory of keys; you can have many of them per account, but the keys they contain are separate and cannot be shared between instances.
The [IBM Cloud CLI](https://cloud.ibm.com/docs/cli?topic=cloud-cli-getting-started) can be used to find the UUID for your KeyProtect instance.
```sh
$ ic resource service-instances
OK
Name Location State Type
Key Protect-private us-south active service_instance
Key Protect-abc123 us-east active service_instance
```
Find the name of your KeyProtect instance as you created it, and the use the client to get its details. The Instance ID is the GUID field, or if you do not see GUID, it will be the last part of the CRN. For example:
```sh
$ ic resource service-instance "Key Protect-private"
OK
Name: Key Protect-private
ID: crn:v1:bluemix:public:kms:us-south:a/.......:1234abcd-906d-438a-8a68-deadbeef1a2b3::
GUID: 1234abcd-906d-438a-8a68-deadbeef1a2b3
```
## Examples
### Generating a root key (CRK)
```go
// Create a root key named MyRootKey with no expiration
key, err := client.CreateRootKey(ctx, "MyRootKey", nil)
if err != nil {
fmt.Println(err)
}
fmt.Println(key.ID, key.Name)
crkID := key.ID
```
### Wrapping and Unwrapping a DEK using a specific Root Key.
```go
myDEK := []byte{"thisisadataencryptionkey"}
// Do some encryption with myDEK
// Wrap the DEK so we can safely store it
wrappedDEK, err := client.Wrap(ctx, crkID, myDEK, nil)
// Unwrap the DEK
dek, err := client.Unwrap(ctx, crkID, wrappedDEK, nil)
// Do some encryption/decryption using the DEK
// Discard the DEK
dek = nil
```
Note you can also pass additional authentication data (AAD) to wrap and unwrap calls
to provide another level of protection for your DEK. The AAD is a string array with
each element up to 255 chars. For example:
```go
myAAD := []string{"First aad string", "second aad string", "third aad string"}
myDEK := []byte{"thisisadataencryptionkey"}
// Do some encryption with myDEK
// Wrap the DEK so we can safely store it
wrappedDEK, err := client.Wrap(ctx, crkID, myDEK, &myAAD)
// Unwrap the DEK
dek, err := client.Unwrap(ctx, crkID, wrappedDEK, &myAAD)
// Do some encryption/decryption using the DEK
// Discard the DEK
dek = nil
```
Have key protect create a DEK for you:
```go
dek, wrappedDek, err := client.WrapCreateDEK(ctx, crkID, nil)
// Do some encrypt/decrypt with the dek
// Discard the DEK
dek = nil
// Save the wrapped DEK for later. Use Unwrap to use it.
```
Can also specify AAD:
```go
myAAD := []string{"First aad string", "second aad string", "third aad string"}
dek, wrappedDek, err := client.WrapCreateDEK(ctx, crkID, &myAAD)
// Do some encrypt/decrypt with the dek
// Discard the DEK
dek = nil
// Save the wrapped DEK for later. Call Unwrap to use it, make
// sure to specify the same AAD.
```

252
vendor/github.com/IBM/keyprotect-go-client/iam/iam.go generated vendored Normal file
View File

@ -0,0 +1,252 @@
// Copyright 2019 IBM Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package iam
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"sync"
"time"
rhttp "github.com/hashicorp/go-retryablehttp"
)
// IAMTokenURL is the global endpoint URL for the IAM token service
const IAMTokenURL = "https://iam.cloud.ibm.com/oidc/token"
var (
// RetryWaitMax is the maximum time to wait between HTTP retries
RetryWaitMax = 30 * time.Second
// RetryMax is the max number of attempts to retry for failed HTTP requests
RetryMax = 4
)
type TokenSource interface {
Token() (*Token, error)
}
// CredentialFromAPIKey returns an IAMTokenSource that requests access tokens
// from the default token endpoint using an IAM API Key as the authentication mechanism
func CredentialFromAPIKey(apiKey string) *IAMTokenSource {
return &IAMTokenSource{
TokenURL: IAMTokenURL,
APIKey: apiKey,
}
}
// Token represents an IAM credential used to authorize requests to another service.
type Token struct {
AccessToken string
RefreshToken string
TokenType string
Expiry time.Time
}
func (t *Token) Valid() bool {
if t == nil || t.AccessToken == "" {
return false
}
if t.Expiry.Before(time.Now()) {
return false
}
return true
}
// jsonToken is for deserializing the token from the response body
type jsonToken struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
TokenType string `json:"token_type"`
ExpiresIn int32 `json:"expires_in"`
}
// getExpireTime uses local time and the ExpiresIn offset to calculate an
// expiration time based off our local clock, which is more accurate for
// us to determine when it expires relative to our client.
// we also pad the time a bit, because long running requests can fail
// mid-request if we send a soon-to-expire token along
func (jt jsonToken) getExpireTime() time.Time {
// set the expiration time for 1 min less than the
// actual time to prevent timeout errors
return time.Now().Add(time.Duration(jt.ExpiresIn-60) * time.Second)
}
// IAMTokenSource is used to retrieve access tokens from the IAM token service.
// Most will probably want to use CredentialFromAPIKey to build an IAMTokenSource type,
// but it can also be created directly if one wishes to override the default IAM
// endpoint by setting TokenURL
type IAMTokenSource struct {
TokenURL string
APIKey string
mu sync.Mutex
t *Token
}
// Token requests an access token from IAM using the IAMTokenSource config.
func (ts *IAMTokenSource) Token() (*Token, error) {
ts.mu.Lock()
defer ts.mu.Unlock()
if ts.t.Valid() {
return ts.t, nil
}
if ts.APIKey == "" {
return nil, errors.New("iam: APIKey is empty")
}
v := url.Values{}
v.Set("grant_type", "urn:ibm:params:oauth:grant-type:apikey")
v.Set("apikey", ts.APIKey)
reqBody := []byte(v.Encode())
u, err := url.Parse(ts.TokenURL)
if err != nil {
return nil, err
}
// NewRequest will calculate Content-Length if we pass it a bytes.Buffer
// instead of a io.Reader type
bodyBuf := bytes.NewBuffer(reqBody)
request, err := rhttp.NewRequest("POST", u.String(), bodyBuf)
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.Header.Set("Accept", "application/json")
// use hashicorp retryable client with max wait time and attempts from module vars
client := rhttp.NewClient()
client.Logger = nil
client.RetryWaitMax = RetryWaitMax
client.RetryMax = RetryMax
client.ErrorHandler = rhttp.PassthroughErrorHandler
// need to use the go http DefaultTransport for tests to override with stubs (gock HTTP stubbing)
client.HTTPClient = &http.Client{
Timeout: time.Duration(60) * time.Second,
}
// this is the DefaultRetryPolicy but with retry on 429s as well
client.CheckRetry = func(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()
}
if err != nil {
return true, err
}
// retry on connection error (code == 0), all 500s except 501, and 429s
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) || resp.StatusCode == 429 {
return true, nil
}
return false, nil
}
resp, err := client.Do(request)
if err != nil {
return nil, err
}
buf := new(bytes.Buffer)
if _, err := buf.ReadFrom(resp.Body); err != nil {
return nil, err
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
var iamErr Error
if err = json.Unmarshal(buf.Bytes(), &iamErr); err != nil {
return nil, err
}
iamErr.HTTPResponse = resp
return nil, iamErr
}
var jToken jsonToken
if err = json.Unmarshal(buf.Bytes(), &jToken); err != nil {
return nil, err
}
token := &Token{
AccessToken: jToken.AccessToken,
RefreshToken: jToken.RefreshToken,
TokenType: jToken.TokenType,
Expiry: jToken.getExpireTime(),
}
ts.t = token
return token, nil
}
// Error is a type to hold error information that the IAM services sends back
// when a request cannot be completed. ErrorCode, ErrorMessage, and Context.RequestID
// are probably the most useful fields. IAM will most likely ask you for the RequestID
// if you ask for support.
//
// Also of note is that the http.Response object is included in HTTPResponse for
// error handling at the higher application levels.
type Error struct {
ErrorCode string `json:"errorCode"`
ErrorMessage string `json:"errorMessage"`
Context *iamRequestContext `json:"context"`
HTTPResponse *http.Response
}
type iamRequestContext struct {
ClientIP string `json:"clientIp"`
ClusterName string `json:"clusterName"`
Host string `json:"host"`
InstanceID string `json:"instanceId"`
RequestID string `json:"requestId"`
RequestType string `json:"requestType"`
ElapsedTime string `json:"elapsedTime"`
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
ThreadID string `json:"threadId"`
URL string `json:"url"`
UserAgent string `json:"userAgent"`
Locale string `json:"locale"`
}
func (ie Error) Error() string {
reqId := ""
if ie.Context != nil {
reqId = ie.Context.RequestID
}
statusCode := 0
if ie.HTTPResponse != nil {
statusCode = ie.HTTPResponse.StatusCode
}
return fmt.Sprintf("iam.Error: HTTP %d requestId='%s' message='%s %s'",
statusCode, reqId, ie.ErrorCode, ie.ErrorMessage)
}

View File

@ -0,0 +1,219 @@
// Copyright 2019 IBM Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kp
import (
"bytes"
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"hash"
"io"
"time"
)
const importTokenEncAlgo = "RSAES_OAEP_SHA_256" // currently the only one supported
// ImportTokenCreateRequest represents request parameters for creating a
// ImportToken.
type ImportTokenCreateRequest struct {
MaxAllowedRetrievals int `json:"maxAllowedRetrievals,omitempty"`
ExpiresInSeconds int `json:"expiration,omitempty"`
}
// ImportTokenKeyResponse represents the response body for various ImportToken
// API calls.
type ImportTokenKeyResponse struct {
ID string `json:"id"`
CreationDate *time.Time `json:"creationDate"`
ExpirationDate *time.Time `json:"expirationDate"`
Payload string `json:"payload"`
Nonce string `json:"nonce"`
}
// ImportTokenMetadata represents the metadata of a ImportToken.
type ImportTokenMetadata struct {
ID string `json:"id"`
CreationDate *time.Time `json:"creationDate"`
ExpirationDate *time.Time `json:"expirationDate"`
MaxAllowedRetrievals int `json:"maxAllowedRetrievals"`
RemainingRetrievals int `json:"remainingRetrievals"`
}
// CreateImportToken creates a key ImportToken.
func (c *Client) CreateImportToken(ctx context.Context, expiration, maxAllowedRetrievals int) (*ImportTokenMetadata, error) {
reqBody := ImportTokenCreateRequest{
MaxAllowedRetrievals: maxAllowedRetrievals,
ExpiresInSeconds: expiration,
}
req, err := c.newRequest("POST", "import_token", &reqBody)
if err != nil {
return nil, err
}
res := ImportTokenMetadata{}
if _, err := c.do(ctx, req, &res); err != nil {
return nil, err
}
return &res, nil
}
// GetImportTokenTransportKey retrieves the ImportToken transport key.
func (c *Client) GetImportTokenTransportKey(ctx context.Context) (*ImportTokenKeyResponse, error) {
res := ImportTokenKeyResponse{}
req, err := c.newRequest("GET", "import_token", nil)
if err != nil {
return nil, err
}
if _, err := c.do(ctx, req, &res); err != nil {
return nil, err
}
return &res, nil
}
// EncryptNonce will wrap the KP generated nonce with the users key-material
func EncryptNonce(key, value, iv string) (string, string, error) {
return encryptNonce(key, value, iv)
}
// EncryptKey will encrypt the user key-material with the public key from key protect
func EncryptKey(key, pubkey string) (string, error) {
return encryptKey(key, pubkey)
}
func encryptNonce(key, value, iv string) (string, string, error) {
var cipherText []byte
pubKey, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return "", "", fmt.Errorf("Failed to decode public key: %s", err)
}
nonce, err := base64.StdEncoding.DecodeString(value)
if err != nil {
return "", "", fmt.Errorf("Failed to decode nonce: %s", err)
}
block, err := aes.NewCipher(pubKey)
if err != nil {
return "", "", err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return "", "", err
}
if iv == "" {
newIv := make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, newIv); err != nil {
panic(err.Error())
}
cipherText = aesgcm.Seal(nil, newIv, nonce, nil)
return base64.StdEncoding.EncodeToString(cipherText), base64.StdEncoding.EncodeToString(newIv), nil
}
cipherText = aesgcm.Seal(nil, []byte(iv), nonce, nil)
return base64.StdEncoding.EncodeToString(cipherText), iv, nil
}
// EncryptNonceWithCBCPAD encrypts the nonce using the user's key-material
// with CBC encrypter. It will also pad the nonce using pkcs7. This is needed
// for Hyper Protect Crypto Services, since it supports only CBC Encryption.
func EncryptNonceWithCBCPAD(key, value, iv string) (string, string, error) {
keyMat, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return "", "", fmt.Errorf("Failed to decode Key: %s", err)
}
nonce, err := base64.StdEncoding.DecodeString(value)
if err != nil {
return "", "", fmt.Errorf("Failed to decode Nonce: %s", err)
}
block, err := aes.NewCipher(keyMat)
if err != nil {
return "", "", err
}
// PKCS7 Padding
paddingLength := aes.BlockSize - (len(nonce) % aes.BlockSize)
paddingBytes := []byte{byte(paddingLength)}
paddingText := bytes.Repeat(paddingBytes, paddingLength)
nonce = append(nonce, paddingText...)
var newIv []byte
if iv != "" {
newIv = []byte(iv)
} else {
newIv = make([]byte, aes.BlockSize)
// Generate an IV to achieve semantic security
if _, err := io.ReadFull(rand.Reader, newIv); err != nil {
return "", "", fmt.Errorf("Failed to generate IV: %s", err)
}
}
cipherText := make([]byte, len(nonce))
mode := cipher.NewCBCEncrypter(block, newIv)
mode.CryptBlocks(cipherText, nonce)
return base64.StdEncoding.EncodeToString(cipherText), base64.StdEncoding.EncodeToString(newIv), nil
}
// encryptKey uses sha256 to encrypt the key
func encryptKey(key, pubKey string) (string, error) {
return encryptKeyWithSHA(key, pubKey, sha256.New())
}
// EncryptKeyWithSHA1 uses sha1 to encrypt the key
func EncryptKeyWithSHA1(key, pubKey string) (string, error) {
return encryptKeyWithSHA(key, pubKey, sha1.New())
}
func encryptKeyWithSHA(key, pubKey string, sha hash.Hash) (string, error) {
decodedPubKey, err := base64.StdEncoding.DecodeString(pubKey)
if err != nil {
return "", fmt.Errorf("Failed to decode public key: %s", err)
}
keyMat, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return "", fmt.Errorf("Failed to decode key material: %s", err)
}
pubKeyBlock, _ := pem.Decode(decodedPubKey)
if pubKeyBlock == nil {
return "", fmt.Errorf("Failed to decode public key into pem format: %s", err)
}
parsedPubKey, err := x509.ParsePKIXPublicKey(pubKeyBlock.Bytes)
if err != nil {
return "", fmt.Errorf("Failed to parse public key: %s", err)
}
publicKey, isRSAPublicKey := parsedPubKey.(*rsa.PublicKey)
if !isRSAPublicKey {
return "", fmt.Errorf("invalid public key")
}
encryptedKey, err := rsa.EncryptOAEP(sha, rand.Reader, publicKey, keyMat, []byte(""))
if err != nil {
return "", fmt.Errorf("Failed to encrypt key: %s", err)
}
return base64.StdEncoding.EncodeToString(encryptedKey), nil
}

543
vendor/github.com/IBM/keyprotect-go-client/instances.go generated vendored Normal file
View File

@ -0,0 +1,543 @@
// Copyright 2019 IBM Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kp
import (
"context"
"fmt"
"net/url"
"time"
)
const (
// DualAuthDelete defines the policy type as dual auth delete
DualAuthDelete = "dualAuthDelete"
// AllowedNetwork defines the policy type as allowed network
AllowedNetwork = "allowedNetwork"
// AllowedIP defines the policy type as allowed ip that are whitelisted
AllowedIP = "allowedIP"
// Metrics defines the policy type as metrics
Metrics = "metrics"
// KeyAccess defines the policy type as key create import access
KeyCreateImportAccess = "keyCreateImportAccess"
// KeyAccess policy attributes
CreateRootKey = "CreateRootKey"
CreateStandardKey = "CreateStandardKey"
ImportRootKey = "ImportRootKey"
ImportStandardKey = "ImportStandardKey"
EnforceToken = "EnforceToken"
)
// InstancePolicy represents a instance-level policy of a key as returned by the KP API.
// this policy enables dual authorization for deleting a key
type InstancePolicy struct {
CreatedBy string `json:"createdBy,omitempty"`
CreatedAt *time.Time `json:"creationDate,omitempty"`
UpdatedAt *time.Time `json:"lastUpdated,omitempty"`
UpdatedBy string `json:"updatedBy,omitempty"`
PolicyType string `json:"policy_type,omitempty"`
PolicyData PolicyData `json:"policy_data,omitempty" mapstructure:"policyData"`
}
// PolicyData contains the details of the policy type
type PolicyData struct {
Enabled *bool `json:"enabled,omitempty"`
Attributes *Attributes `json:"attributes,omitempty"`
}
// Attributes contains the detals of allowed network policy type
type Attributes struct {
AllowedNetwork *string `json:"allowed_network,omitempty"`
AllowedIP IPAddresses `json:"allowed_ip,omitempty"`
CreateRootKey *bool `json:"create_root_key,omitempty"`
CreateStandardKey *bool `json:"create_standard_key,omitempty"`
ImportRootKey *bool `json:"import_root_key,omitempty"`
ImportStandardKey *bool `json:"import_standard_key,omitempty"`
EnforceToken *bool `json:"enforce_token,omitempty"`
}
// IPAddresses ...
type IPAddresses []string
// InstancePolicies represents a collection of Policies associated with Key Protect instances.
type InstancePolicies struct {
Metadata PoliciesMetadata `json:"metadata"`
Policies []InstancePolicy `json:"resources"`
}
// GetDualAuthInstancePolicy retrieves the dual auth delete policy details associated with the instance
// For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-manage-dual-auth
func (c *Client) GetDualAuthInstancePolicy(ctx context.Context) (*InstancePolicy, error) {
policyResponse := InstancePolicies{}
err := c.getInstancePolicy(ctx, DualAuthDelete, &policyResponse)
if err != nil {
return nil, err
}
if len(policyResponse.Policies) == 0 {
return nil, nil
}
return &policyResponse.Policies[0], nil
}
// GetAllowedNetworkInstancePolicy retrieves the allowed network policy details associated with the instance.
// For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-managing-network-access-policies
func (c *Client) GetAllowedNetworkInstancePolicy(ctx context.Context) (*InstancePolicy, error) {
policyResponse := InstancePolicies{}
err := c.getInstancePolicy(ctx, AllowedNetwork, &policyResponse)
if err != nil {
return nil, err
}
if len(policyResponse.Policies) == 0 {
return nil, nil
}
return &policyResponse.Policies[0], nil
}
// GetAllowedIPInstancePolicy retrieves the allowed IP instance policy details associated with the instance.
// For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-manage-allowed-ip
func (c *Client) GetAllowedIPInstancePolicy(ctx context.Context) (*InstancePolicy, error) {
policyResponse := InstancePolicies{}
err := c.getInstancePolicy(ctx, AllowedIP, &policyResponse)
if err != nil {
return nil, err
}
if len(policyResponse.Policies) == 0 {
return nil, nil
}
return &policyResponse.Policies[0], nil
}
// GetKeyCreateImportAccessInstancePolicy retrieves the key create import access policy details associated with the instance.
// For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-manage-keyCreateImportAccess
func (c *Client) GetKeyCreateImportAccessInstancePolicy(ctx context.Context) (*InstancePolicy, error) {
policyResponse := InstancePolicies{}
err := c.getInstancePolicy(ctx, KeyCreateImportAccess, &policyResponse)
if err != nil {
return nil, err
}
if len(policyResponse.Policies) == 0 {
return nil, nil
}
return &policyResponse.Policies[0], nil
}
func (c *Client) getInstancePolicy(ctx context.Context, policyType string, policyResponse *InstancePolicies) error {
req, err := c.newRequest("GET", "instance/policies", nil)
if err != nil {
return err
}
v := url.Values{}
v.Set("policy", policyType)
req.URL.RawQuery = v.Encode()
_, err = c.do(ctx, req, &policyResponse)
return err
}
// GetMetricsInstancePolicy retrieves the metrics policy details associated with the instance
// For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-manage-sysdig-metrics
func (c *Client) GetMetricsInstancePolicy(ctx context.Context) (*InstancePolicy, error) {
policyResponse := InstancePolicies{}
err := c.getInstancePolicy(ctx, Metrics, &policyResponse)
if err != nil {
return nil, err
}
if len(policyResponse.Policies) == 0 {
return nil, nil
}
return &policyResponse.Policies[0], nil
}
// GetInstancePolicies retrieves all policies of an Instance.
func (c *Client) GetInstancePolicies(ctx context.Context) ([]InstancePolicy, error) {
policyresponse := InstancePolicies{}
req, err := c.newRequest("GET", "instance/policies", nil)
if err != nil {
return nil, err
}
_, err = c.do(ctx, req, &policyresponse)
if err != nil {
return nil, err
}
return policyresponse.Policies, nil
}
func (c *Client) setInstancePolicy(ctx context.Context, policyType string, policyRequest InstancePolicies) error {
req, err := c.newRequest("PUT", "instance/policies", &policyRequest)
if err != nil {
return err
}
v := url.Values{}
v.Set("policy", policyType)
req.URL.RawQuery = v.Encode()
policiesResponse := InstancePolicies{}
_, err = c.do(ctx, req, &policiesResponse)
return err
}
// SetDualAuthInstancePolicy updates the dual auth delete policy details associated with an instance
// For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-manage-dual-auth
func (c *Client) SetDualAuthInstancePolicy(ctx context.Context, enable bool) error {
policy := InstancePolicy{
PolicyType: DualAuthDelete,
PolicyData: PolicyData{
Enabled: &enable,
},
}
policyRequest := InstancePolicies{
Metadata: PoliciesMetadata{
CollectionType: policyType,
NumberOfPolicies: 1,
},
Policies: []InstancePolicy{policy},
}
err := c.setInstancePolicy(ctx, DualAuthDelete, policyRequest)
return err
}
// SetAllowedIPInstancePolices updates the allowed IP instance policy details associated with an instance.
// For more information can refet to the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-manage-allowed-ip
func (c *Client) SetAllowedIPInstancePolicy(ctx context.Context, enable bool, allowedIPs []string) error {
policy := InstancePolicy{
PolicyType: AllowedIP,
PolicyData: PolicyData{
Enabled: &enable,
},
}
// The IP address validation is performed by the key protect service.
if enable && len(allowedIPs) != 0 {
policy.PolicyData.Attributes = &Attributes{}
policy.PolicyData.Attributes.AllowedIP = allowedIPs
} else if enable && len(allowedIPs) == 0 {
return fmt.Errorf("Please provide at least 1 IP subnet specified with CIDR notation")
} else if !enable && len(allowedIPs) != 0 {
return fmt.Errorf("IP address list should only be provided if the policy is being enabled")
}
policyRequest := InstancePolicies{
Metadata: PoliciesMetadata{
CollectionType: policyType,
NumberOfPolicies: 1,
},
Policies: []InstancePolicy{policy},
}
err := c.setInstancePolicy(ctx, AllowedIP, policyRequest)
return err
}
// SetAllowedNetWorkInstancePolicy updates the allowed network policy details associated with an instance
// For more information can refer to the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-managing-network-access-policies
func (c *Client) SetAllowedNetworkInstancePolicy(ctx context.Context, enable bool, networkType string) error {
policy := InstancePolicy{
PolicyType: AllowedNetwork,
PolicyData: PolicyData{
Enabled: &enable,
Attributes: &Attributes{},
},
}
if networkType != "" {
policy.PolicyData.Attributes.AllowedNetwork = &networkType
}
policyRequest := InstancePolicies{
Metadata: PoliciesMetadata{
CollectionType: policyType,
NumberOfPolicies: 1,
},
Policies: []InstancePolicy{policy},
}
err := c.setInstancePolicy(ctx, AllowedNetwork, policyRequest)
return err
}
// SetMetricsInstancePolicy updates the metrics policy details associated with an instance
// For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-manage-sysdig-metrics
func (c *Client) SetMetricsInstancePolicy(ctx context.Context, enable bool) error {
policy := InstancePolicy{
PolicyType: Metrics,
PolicyData: PolicyData{
Enabled: &enable,
},
}
policyRequest := InstancePolicies{
Metadata: PoliciesMetadata{
CollectionType: policyType,
NumberOfPolicies: 1,
},
Policies: []InstancePolicy{policy},
}
err := c.setInstancePolicy(ctx, Metrics, policyRequest)
if err != nil {
return err
}
return err
}
// SetKeyCreateImportAccessInstancePolicy updates the key create import access policy details associated with an instance.
// For more information, please refer to the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-manage-keyCreateImportAccess
func (c *Client) SetKeyCreateImportAccessInstancePolicy(ctx context.Context, enable bool, attributes map[string]bool) error {
policy := InstancePolicy{
PolicyType: KeyCreateImportAccess,
PolicyData: PolicyData{
Enabled: &enable,
},
}
if enable {
policy.PolicyData.Attributes = &Attributes{}
a := policy.PolicyData.Attributes
if val, ok := attributes[CreateRootKey]; ok {
a.CreateRootKey = &val
}
if val, ok := attributes[CreateStandardKey]; ok {
a.CreateStandardKey = &val
}
if val, ok := attributes[ImportRootKey]; ok {
a.ImportRootKey = &val
}
if val, ok := attributes[ImportStandardKey]; ok {
a.ImportStandardKey = &val
}
if val, ok := attributes[EnforceToken]; ok {
a.EnforceToken = &val
}
}
policyRequest := InstancePolicies{
Metadata: PoliciesMetadata{
CollectionType: policyType,
NumberOfPolicies: 1,
},
Policies: []InstancePolicy{policy},
}
err := c.setInstancePolicy(ctx, KeyCreateImportAccess, policyRequest)
return err
}
// BasicPolicyData defines the attribute input for the policy that supports only enabled parameter
type BasicPolicyData struct {
Enabled bool
}
// AllowedNetworkPolicyData defines the attribute input for the Allowed Network instance policy
type AllowedNetworkPolicyData struct {
Enabled bool
Network string
}
// AllowedIPPolicyData defines the attribute input for the Allowed IP instance policy
type AllowedIPPolicyData struct {
Enabled bool
IPAddresses IPAddresses
}
// KeyAccessInstancePolicyData defines the attribute input for the Key Create Import Access instance policy
type KeyCreateImportAccessInstancePolicy struct {
Enabled bool
CreateRootKey bool
CreateStandardKey bool
ImportRootKey bool
ImportStandardKey bool
EnforceToken bool
}
// MultiplePolicies defines the input for the SetInstancPolicies method that can hold multiple policy details
type MultiplePolicies struct {
DualAuthDelete *BasicPolicyData
AllowedNetwork *AllowedNetworkPolicyData
AllowedIP *AllowedIPPolicyData
Metrics *BasicPolicyData
KeyCreateImportAccess *KeyCreateImportAccessInstancePolicy
}
// SetInstancePolicies updates single or multiple policy details of an instance.
func (c *Client) SetInstancePolicies(ctx context.Context, policies MultiplePolicies) error {
var resPolicies []InstancePolicy
if policies.DualAuthDelete != nil {
policy := InstancePolicy{
PolicyType: DualAuthDelete,
PolicyData: PolicyData{
Enabled: &(policies.DualAuthDelete.Enabled),
},
}
resPolicies = append(resPolicies, policy)
}
if policies.AllowedNetwork != nil {
policy := InstancePolicy{
PolicyType: AllowedNetwork,
PolicyData: PolicyData{
Enabled: &(policies.AllowedNetwork.Enabled),
Attributes: &Attributes{
AllowedNetwork: &(policies.AllowedNetwork.Network),
},
},
}
resPolicies = append(resPolicies, policy)
}
if policies.AllowedIP != nil {
policy := InstancePolicy{
PolicyType: AllowedIP,
PolicyData: PolicyData{
Enabled: &(policies.AllowedIP.Enabled),
Attributes: &Attributes{
AllowedIP: policies.AllowedIP.IPAddresses,
},
},
}
resPolicies = append(resPolicies, policy)
}
if policies.Metrics != nil {
policy := InstancePolicy{
PolicyType: Metrics,
PolicyData: PolicyData{
Enabled: &(policies.Metrics.Enabled),
},
}
resPolicies = append(resPolicies, policy)
}
if policies.KeyCreateImportAccess != nil {
policy := InstancePolicy{
PolicyType: KeyCreateImportAccess,
PolicyData: PolicyData{
Enabled: &(policies.KeyCreateImportAccess.Enabled),
Attributes: &Attributes{},
},
}
if policies.KeyCreateImportAccess.CreateRootKey {
policy.PolicyData.Attributes.CreateRootKey = &policies.KeyCreateImportAccess.CreateRootKey
}
if policies.KeyCreateImportAccess.CreateStandardKey {
policy.PolicyData.Attributes.CreateStandardKey = &policies.KeyCreateImportAccess.CreateStandardKey
}
if policies.KeyCreateImportAccess.ImportRootKey {
policy.PolicyData.Attributes.ImportRootKey = &policies.KeyCreateImportAccess.ImportRootKey
}
if policies.KeyCreateImportAccess.ImportStandardKey {
policy.PolicyData.Attributes.ImportStandardKey = &policies.KeyCreateImportAccess.ImportStandardKey
}
if policies.KeyCreateImportAccess.EnforceToken {
policy.PolicyData.Attributes.EnforceToken = &policies.KeyCreateImportAccess.EnforceToken
}
resPolicies = append(resPolicies, policy)
}
policyRequest := InstancePolicies{
Metadata: PoliciesMetadata{
CollectionType: policyType,
NumberOfPolicies: len(resPolicies),
},
Policies: resPolicies,
}
policyresponse := Policies{}
req, err := c.newRequest("PUT", "instance/policies", &policyRequest)
if err != nil {
return err
}
_, err = c.do(ctx, req, &policyresponse)
return err
}
type portsMetadata struct {
CollectionType string `json:"collectionType"`
NumberOfPorts int `json:"collectionTotal"`
}
type portResponse struct {
Metadata portsMetadata `json:"metadata"`
Ports []privatePort `json:"resources"`
}
type privatePort struct {
PrivatePort int `json:"private_endpoint_port,omitempty"`
}
// GetAllowedIPPrivateNetworkPort retrieves the private endpoint port assigned to allowed ip policy.
func (c *Client) GetAllowedIPPrivateNetworkPort(ctx context.Context) (int, error) {
var portResponse portResponse
req, err := c.newRequest("GET", "instance/allowed_ip_port", nil)
if err != nil {
return 0, err
}
_, err = c.do(ctx, req, &portResponse)
if err != nil {
return 0, err
}
if len(portResponse.Ports) == 0 {
return 0, fmt.Errorf("No port number available. Please check the instance has an enabled allowedIP policy")
}
return portResponse.Ports[0].PrivatePort, nil
}

View File

@ -0,0 +1,68 @@
package kp
import (
"context"
"fmt"
"time"
)
var (
requestPath = "keys/%s/aliases/%s"
)
// KeyAlias represents an Alias details of a key as returned by KP API
type KeyAlias struct {
KeyID string `json:"keyId,omitempty"`
Alias string `json:"alias,omitempty"`
CreatedBy string `json:"createdBy,omitempty"`
CreationDate *time.Time `json:"creationDate,omitempty"`
}
// AliasesMetadata represents the metadata of a collection of aliases
type AliasesMetadata struct {
CollectionType string `json:"collectionType"`
NumberOfAliases int `json:"collectionTotal"`
}
type KeyAliases struct {
Metadata AliasesMetadata `json:"metadata"`
KeyAliases []KeyAlias `json:"resources"`
}
// CreateKeyAlias creates an alias name for a key.
// An alias name acts as an identifier just like key ID
// 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
func (c *Client) CreateKeyAlias(ctx context.Context, aliasName, keyID string) (*KeyAlias, error) {
req, err := c.newRequest("POST", fmt.Sprintf(requestPath, keyID, aliasName), nil)
if err != nil {
return nil, err
}
aliasesResponse := KeyAliases{}
_, err = c.do(ctx, req, &aliasesResponse)
if err != nil {
return nil, err
}
if len(aliasesResponse.KeyAliases) == 0 {
return nil, nil
}
return &aliasesResponse.KeyAliases[0], nil
}
// DeleteKeyAlias deletes an alias name associated with a key
// 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
func (c *Client) DeleteKeyAlias(ctx context.Context, aliasName, keyID string) error {
req, err := c.newRequest("DELETE", fmt.Sprintf(requestPath, keyID, aliasName), nil)
if err != nil {
return err
}
_, err = c.do(ctx, req, nil)
return err
}

View File

@ -0,0 +1,75 @@
package kp
import (
"context"
"fmt"
"time"
)
const (
path = "key_rings"
)
type KeyRing struct {
ID string `json:"id,omitempty"`
CreationDate *time.Time `json:"creationDate,omitempty"`
CreatedBy string `json:"createdBy,omitempty"`
}
type KeyRings struct {
Metadata KeysMetadata `json:"metadata"`
KeyRings []KeyRing `json:"resources"`
}
// CreateRing method creates a key ring in the instance with the provided name
// For information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-managing-key-rings#create-key-ring-api
func (c *Client) CreateKeyRing(ctx context.Context, id string) error {
req, err := c.newRequest("POST", fmt.Sprintf(path+"/%s", id), nil)
if err != nil {
return err
}
_, err = c.do(ctx, req, nil)
if err != nil {
return err
}
return nil
}
// GetRings method retrieves all the key rings associated with the instance
// For information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-managing-key-rings#list-key-ring-api
func (c *Client) GetKeyRings(ctx context.Context) (*KeyRings, error) {
rings := KeyRings{}
req, err := c.newRequest("GET", path, nil)
if err != nil {
return nil, err
}
_, err = c.do(ctx, req, &rings)
if err != nil {
return nil, err
}
return &rings, nil
}
// DeleteRing method deletes the key ring with the provided name in the instance
// For information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-managing-key-rings#delete-key-ring-api
func (c *Client) DeleteKeyRing(ctx context.Context, id string) error {
req, err := c.newRequest("DELETE", fmt.Sprintf(path+"/%s", id), nil)
if err != nil {
return err
}
_, err = c.do(ctx, req, nil)
if err != nil {
return err
}
return nil
}

463
vendor/github.com/IBM/keyprotect-go-client/keys.go generated vendored Normal file
View File

@ -0,0 +1,463 @@
// Copyright 2019 IBM Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kp
import (
"context"
"encoding/base64"
"fmt"
"log"
"net/url"
"strconv"
"time"
)
const (
ReturnMinimal PreferReturn = 0
ReturnRepresentation PreferReturn = 1
keyType = "application/vnd.ibm.kms.key+json"
)
var (
preferHeaders = []string{"return=minimal", "return=representation"}
)
// PreferReturn designates the value for the "Prefer" header.
type PreferReturn int
// Key represents a key as returned by the KP API.
type Key struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Type string `json:"type,omitempty"`
Tags []string `json:"Tags,omitempty"`
Aliases []string `json:"aliases,omitempty"`
AlgorithmType string `json:"algorithmType,omitempty"`
CreatedBy string `json:"createdBy,omitempty"`
CreationDate *time.Time `json:"creationDate,omitempty"`
LastUpdateDate *time.Time `json:"lastUpdateDate,omitempty"`
LastRotateDate *time.Time `json:"lastRotateDate,omitempty"`
KeyVersion *KeyVersion `json:"keyVersion,omitempty" mapstructure:keyVersion`
KeyRingID string `json:"keyRingID,omitempty"`
Extractable bool `json:"extractable"`
Expiration *time.Time `json:"expirationDate,omitempty"`
Imported bool `json:"imported,omitempty"`
Payload string `json:"payload,omitempty"`
State int `json:"state,omitempty"`
EncryptionAlgorithm string `json:"encryptionAlgorithm,omitempty"`
CRN string `json:"crn,omitempty"`
EncryptedNonce string `json:"encryptedNonce,omitempty"`
IV string `json:"iv,omitempty"`
Deleted *bool `json:"deleted,omitempty"`
DeletedBy *string `json:"deletedBy,omitempty"`
DeletionDate *time.Time `json:"deletionDate,omitempty"`
DualAuthDelete *DualAuth `json:"dualAuthDelete,omitempty"`
}
// KeysMetadata represents the metadata of a collection of keys.
type KeysMetadata struct {
CollectionType string `json:"collectionType"`
NumberOfKeys int `json:"collectionTotal"`
}
// Keys represents a collection of Keys.
type Keys struct {
Metadata KeysMetadata `json:"metadata"`
Keys []Key `json:"resources"`
}
// KeysActionRequest represents request parameters for a key action
// API call.
type KeysActionRequest struct {
PlainText string `json:"plaintext,omitempty"`
AAD []string `json:"aad,omitempty"`
CipherText string `json:"ciphertext,omitempty"`
Payload string `json:"payload,omitempty"`
}
type KeyVersion struct {
ID string `json:"id,omitempty"`
CreationDate *time.Time `json:"creationDate,omitempty"`
}
// CreateKey creates a new KP key.
func (c *Client) CreateKey(ctx context.Context, name string, expiration *time.Time, extractable bool) (*Key, error) {
return c.CreateImportedKey(ctx, name, expiration, "", "", "", extractable)
}
// 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) {
key := Key{
Name: name,
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
}
return c.createKey(ctx, key)
}
// CreateRootKey creates a new, non-extractable key resource without
// key material.
func (c *Client) CreateRootKey(ctx context.Context, name string, expiration *time.Time) (*Key, error) {
return c.CreateKey(ctx, name, expiration, false)
}
// CreateStandardKey creates a new, extractable key resource without
// key material.
func (c *Client) CreateStandardKey(ctx context.Context, name string, expiration *time.Time) (*Key, error) {
return c.CreateKey(ctx, name, expiration, true)
}
// CreateImportedRootKey creates a new, non-extractable key resource
// with the given key material.
func (c *Client) CreateImportedRootKey(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string) (*Key, error) {
return c.CreateImportedKey(ctx, name, expiration, payload, encryptedNonce, iv, false)
}
// CreateStandardKey creates a new, extractable key resource with the
// given key material.
func (c *Client) CreateImportedStandardKey(ctx context.Context, name string, expiration *time.Time, payload string) (*Key, error) {
return c.CreateImportedKey(ctx, name, expiration, payload, "", "", true)
}
// CreateKeyWithAliaes creats a new key with alias names. A key can have a maximum of 5 alias names.
// For more information please refer to the links below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-create-root-keys#create-root-key-api
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-create-standard-keys#create-standard-key-api
func (c *Client) CreateKeyWithAliases(ctx context.Context, name string, expiration *time.Time, extractable bool, aliases []string) (*Key, error) {
return c.CreateImportedKeyWithAliases(ctx, name, expiration, "", "", "", extractable, aliases)
}
// CreateImportedKeyWithAliases creates a new key with alias name and provided key material. A key can have a maximum of 5 alias names
// When importing root keys with import-token encryptedNonce and iv need to passed along with payload.
// Standard Keys cannot be imported with an import token hence only payload is required.
// For more information please refer to the links below:
// 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
func (c *Client) CreateImportedKeyWithAliases(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool, aliases []string) (*Key, error) {
key := Key{
Name: name,
Type: keyType,
Extractable: extractable,
Payload: payload,
Aliases: aliases,
}
if !extractable && payload != "" && encryptedNonce != "" && iv != "" {
key.EncryptedNonce = encryptedNonce
key.IV = iv
key.EncryptionAlgorithm = importTokenEncAlgo
}
if expiration != nil {
key.Expiration = expiration
}
return c.createKey(ctx, key)
}
func (c *Client) createKey(ctx context.Context, key Key) (*Key, error) {
keysRequest := Keys{
Metadata: KeysMetadata{
CollectionType: keyType,
NumberOfKeys: 1,
},
Keys: []Key{key},
}
req, err := c.newRequest("POST", "keys", &keysRequest)
if err != nil {
return nil, err
}
keysResponse := Keys{}
if _, err := c.do(ctx, req, &keysResponse); err != nil {
return nil, err
}
return &keysResponse.Keys[0], nil
}
// GetKeys retrieves a collection of keys that can be paged through.
func (c *Client) GetKeys(ctx context.Context, limit int, offset int) (*Keys, error) {
if limit == 0 {
limit = 2000
}
req, err := c.newRequest("GET", "keys", nil)
if err != nil {
return nil, err
}
v := url.Values{}
v.Set("limit", strconv.Itoa(limit))
v.Set("offset", strconv.Itoa(offset))
req.URL.RawQuery = v.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.
// For more information on Key Alias please refer to the link below
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-retrieve-key
func (c *Client) GetKey(ctx context.Context, idOrAlias string) (*Key, error) {
return c.getKey(ctx, idOrAlias, "keys/%s")
}
// GetKeyMetadata retrieves the metadata of a Key by ID or alias name.
// Note that the "/api/v2/keys/{id}/metadata" API does not return the payload,
// therefore the payload attribute in the Key pointer will always be empty.
// If you need the payload, you need to use the GetKey() function with the
// correct service access role.
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-manage-access#service-access-roles
func (c *Client) GetKeyMetadata(ctx context.Context, idOrAlias string) (*Key, error) {
return c.getKey(ctx, idOrAlias, "keys/%s/metadata")
}
func (c *Client) getKey(ctx context.Context, id string, path string) (*Key, error) {
keys := Keys{}
req, err := c.newRequest("GET", fmt.Sprintf(path, id), nil)
if err != nil {
return nil, err
}
_, err = c.do(ctx, req, &keys)
if err != nil {
return nil, err
}
return &keys.Keys[0], nil
}
type CallOpt interface{}
type ForceOpt struct {
Force bool
}
// DeleteKey deletes a key resource by specifying the ID of the key.
func (c *Client) DeleteKey(ctx context.Context, id string, prefer PreferReturn, callOpts ...CallOpt) (*Key, error) {
req, err := c.newRequest("DELETE", fmt.Sprintf("keys/%s", id), nil)
if err != nil {
return nil, err
}
for _, opt := range callOpts {
switch v := opt.(type) {
case ForceOpt:
params := url.Values{}
params.Set("force", strconv.FormatBool(v.Force))
req.URL.RawQuery = params.Encode()
default:
log.Printf("WARNING: Ignoring invalid CallOpt passed to DeleteKey: %v\n", v)
}
}
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
// This method performs restore of any key from deleted state to active state.
// For more information please refer to the link below:
// https://cloud.ibm.com/dowcs/key-protect?topic=key-protect-restore-keys
func (c *Client) RestoreKey(ctx context.Context, id string) (*Key, error) {
req, err := c.newRequest("POST", fmt.Sprintf("keys/%s/restore", id), nil)
if err != nil {
return nil, err
}
keysResponse := Keys{}
_, err = c.do(ctx, req, &keysResponse)
if err != nil {
return nil, err
}
return &keysResponse.Keys[0], nil
}
// 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) {
_, ct, err := c.wrap(ctx, id, plainText, additionalAuthData)
return ct, err
}
// WrapCreateDEK calls the wrap action without plain text.
func (c *Client) WrapCreateDEK(ctx context.Context, id string, additionalAuthData *[]string) ([]byte, []byte, error) {
return c.wrap(ctx, id, nil, additionalAuthData)
}
func (c *Client) wrap(ctx context.Context, id string, plainText []byte, additionalAuthData *[]string) ([]byte, []byte, error) {
keysActionReq := &KeysActionRequest{}
if plainText != nil {
_, err := base64.StdEncoding.DecodeString(string(plainText))
if err != nil {
return nil, nil, err
}
keysActionReq.PlainText = string(plainText)
}
if additionalAuthData != nil {
keysActionReq.AAD = *additionalAuthData
}
keysAction, err := c.doKeysAction(ctx, id, "wrap", keysActionReq)
if err != nil {
return nil, nil, err
}
pt := []byte(keysAction.PlainText)
ct := []byte(keysAction.CipherText)
return pt, ct, nil
}
// 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) {
plainText, _, err := c.UnwrapV2(ctx, id, cipherText, additionalAuthData)
if err != nil {
return nil, err
}
return plainText, nil
}
// Unwrap with rotation support.
func (c *Client) UnwrapV2(ctx context.Context, id string, cipherText []byte, additionalAuthData *[]string) ([]byte, []byte, error) {
keysAction := &KeysActionRequest{
CipherText: string(cipherText),
}
if additionalAuthData != nil {
keysAction.AAD = *additionalAuthData
}
respAction, err := c.doKeysAction(ctx, id, "unwrap", keysAction)
if err != nil {
return nil, nil, err
}
plainText := []byte(respAction.PlainText)
rewrapped := []byte(respAction.CipherText)
return plainText, rewrapped, nil
}
// Rotate rotates a CRK.
func (c *Client) Rotate(ctx context.Context, id, payload string) error {
actionReq := &KeysActionRequest{
Payload: payload,
}
_, err := c.doKeysAction(ctx, id, "rotate", actionReq)
if err != nil {
return err
}
return nil
}
// 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.
// 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
func (c *Client) DisableKey(ctx context.Context, id string) error {
_, err := c.doKeysAction(ctx, id, "disable", nil)
return err
}
// Enable a key. Only disabled keys can be enabled. After enable
// the key becomes active and key operations can be performed on it.
// Note: This does not recover Deleted keys.
// 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
func (c *Client) EnableKey(ctx context.Context, id string) error {
_, err := c.doKeysAction(ctx, id, "enable", nil)
return err
}
// InitiateDualAuthDelete sets a key for deletion. The key must be configured with a DualAuthDelete policy.
// 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:
// 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 {
_, err := c.doKeysAction(ctx, id, "setKeyForDeletion", nil)
return err
}
// CancelDualAuthDelete unsets the key for deletion. If a key is set for deletion, it can
// be prevented from getting deleted by unsetting the key for deletion.
// 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
func (c *Client) CancelDualAuthDelete(ctx context.Context, id string) error {
_, err := c.doKeysAction(ctx, id, "unsetKeyForDeletion", nil)
return err
}
// 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) {
keyActionRsp := KeysActionRequest{}
v := url.Values{}
v.Set("action", action)
req, err := c.newRequest("POST", fmt.Sprintf("keys/%s", id), keysActionReq)
if err != nil {
return nil, err
}
req.URL.RawQuery = v.Encode()
_, err = c.do(ctx, req, &keyActionRsp)
if err != nil {
return nil, err
}
return &keyActionRsp, nil
}

510
vendor/github.com/IBM/keyprotect-go-client/kp.go generated vendored Normal file
View File

@ -0,0 +1,510 @@
// Copyright 2019 IBM Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// keyprotect-go-client is a Go client library for interacting with the IBM KeyProtect service.
package kp
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/google/uuid"
rhttp "github.com/hashicorp/go-retryablehttp"
"github.com/IBM/keyprotect-go-client/iam"
)
const (
// DefaultBaseURL ...
DefaultBaseURL = "https://us-south.kms.cloud.ibm.com"
// DefaultTokenURL ..
DefaultTokenURL = iam.IAMTokenURL
// VerboseNone ...
VerboseNone = 0
// VerboseBodyOnly ...
VerboseBodyOnly = 1
// VerboseAll ...
VerboseAll = 2
// VerboseFailOnly ...
VerboseFailOnly = 3
// VerboseAllNoRedact ...
VerboseAllNoRedact = 4
authContextKey ContextKey = 0
defaultTimeout = 30 // in seconds.
)
var (
// RetryWaitMax is the maximum time to wait between HTTP retries
RetryWaitMax = 30 * time.Second
// RetryMax is the max number of attempts to retry for failed HTTP requests
RetryMax = 4
cidCtxKey = ctxKey("X-Correlation-Id")
)
type ctxKey string
// ClientConfig ...
type ClientConfig struct {
BaseURL string
Authorization string // The IBM Cloud (Bluemix) access token
APIKey string // Service ID API key, can be used instead of an access token
TokenURL string // The URL used to get an access token from the API key
InstanceID string // The IBM Cloud (Bluemix) instance ID that identifies your Key Protect service instance.
KeyRing string // The ID of the target Key Ring the key is associated with. It is optional but recommended for better performance.
Verbose int // See verbose values above
Timeout float64 // KP request timeout in seconds.
}
// DefaultTransport ...
func DefaultTransport() http.RoundTripper {
transport := &http.Transport{
DisableKeepAlives: true,
MaxIdleConnsPerHost: -1,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false,
},
}
return transport
}
// API is deprecated. Use Client instead.
type API = Client
// Client holds configuration and auth information to interact with KeyProtect.
// It is expected that one of these is created per KeyProtect service instance/credential pair.
type Client struct {
URL *url.URL
HttpClient http.Client
Dump Dump
Config ClientConfig
Logger Logger
tokenSource iam.TokenSource
}
// New creates and returns a Client without logging.
func New(config ClientConfig, transport http.RoundTripper) (*Client, error) {
return NewWithLogger(config, transport, nil)
}
// NewWithLogger creates and returns a Client with logging. The
// error value will be non-nil if the config is invalid.
func NewWithLogger(config ClientConfig, transport http.RoundTripper, logger Logger) (*Client, error) {
if transport == nil {
transport = DefaultTransport()
}
if logger == nil {
logger = NewLogger(func(args ...interface{}) {
fmt.Println(args...)
})
}
if config.Verbose > len(dumpers)-1 || config.Verbose < 0 {
return nil, errors.New("verbose value is out of range")
}
if config.Timeout == 0 {
config.Timeout = defaultTimeout
}
keysURL := fmt.Sprintf("%s/api/v2/", config.BaseURL)
u, err := url.Parse(keysURL)
if err != nil {
return nil, err
}
ts := iam.CredentialFromAPIKey(config.APIKey)
if config.TokenURL != "" {
ts.TokenURL = config.TokenURL
}
c := &Client{
URL: u,
HttpClient: http.Client{
Timeout: time.Duration(config.Timeout * float64(time.Second)),
Transport: transport,
},
Dump: dumpers[config.Verbose],
Config: config,
Logger: logger,
tokenSource: ts,
}
return c, nil
}
func (c *Client) newRequest(method, path string, body interface{}) (*http.Request, error) {
u, err := c.URL.Parse(path)
if err != nil {
return nil, err
}
var reqBody []byte
var buf io.Reader
if body != nil {
reqBody, err = json.Marshal(body)
if err != nil {
return nil, err
}
buf = bytes.NewBuffer(reqBody)
}
request, err := http.NewRequest(method, u.String(), buf)
if err != nil {
return nil, err
}
request.Header.Set("accept", "application/json")
return request, nil
}
type reason struct {
Code string
Message string
Status int
MoreInfo string
}
func (r reason) String() string {
if r.MoreInfo != "" {
return fmt.Sprintf("%s: %s - FOR_MORE_INFO_REFER: %s", r.Code, r.Message, r.MoreInfo)
}
return fmt.Sprintf("%s: %s", r.Code, r.Message)
}
type Error struct {
URL string // URL of request that resulted in this error
StatusCode int // HTTP error code from KeyProtect service
Message string // error message from KeyProtect service
BodyContent []byte // raw body content if more inspection is needed
CorrelationID string // string value of a UUID that uniquely identifies the request to KeyProtect
Reasons []reason // collection of reason types containing detailed error messages
}
// Error returns correlation id and error message string
func (e Error) Error() string {
var extraVars string
if e.Reasons != nil && len(e.Reasons) > 0 {
extraVars = fmt.Sprintf(", reasons='%s'", e.Reasons)
}
return fmt.Sprintf("kp.Error: correlation_id='%v', msg='%s'%s", e.CorrelationID, e.Message, extraVars)
}
// URLError wraps an error from client.do() calls with a correlation ID from KeyProtect
type URLError struct {
Err error
CorrelationID string
}
func (e URLError) Error() string {
return fmt.Sprintf(
"error during request to KeyProtect correlation_id='%s': %s", e.CorrelationID, e.Err.Error())
}
func (c *Client) do(ctx context.Context, req *http.Request, res interface{}) (*http.Response, error) {
acccesToken, err := c.getAccessToken(ctx)
if err != nil {
return nil, err
}
// retrieve the correlation id from the context. If not present, then a UUID will be
// generated for the correlation ID and feed it into the request
// KeyProtect will use this when it is set on a request header rather than generating its
// own inside the service
// if not present, we generate our own here because a connection error might actually
// mean the request doesn't make it server side, so having a correlation ID locally helps
// us know that when comparing with server side logs.
corrID := c.getCorrelationID(ctx)
req.Header.Set("bluemix-instance", c.Config.InstanceID)
req.Header.Set("authorization", acccesToken)
req.Header.Set("correlation-id", corrID)
if c.Config.KeyRing != "" {
req.Header.Set("x-kms-key-ring", c.Config.KeyRing)
}
// set request up to be retryable on 500-level http codes and client errors
retryableClient := getRetryableClient(&c.HttpClient)
retryableRequest, err := rhttp.FromRequest(req)
if err != nil {
return nil, err
}
response, err := retryableClient.Do(retryableRequest.WithContext(ctx))
if err != nil {
return nil, &URLError{err, corrID}
}
defer response.Body.Close()
resBody, err := ioutil.ReadAll(response.Body)
redact := []string{c.Config.APIKey, req.Header.Get("authorization")}
c.Dump(req, response, []byte{}, resBody, c.Logger, redact)
if err != nil {
return nil, err
}
type KPErrorMsg struct {
Message string `json:"errorMsg,omitempty"`
Reasons []reason
}
type KPError struct {
Resources []KPErrorMsg `json:"resources,omitempty"`
}
switch response.StatusCode {
case http.StatusCreated:
if len(resBody) != 0 {
if err := json.Unmarshal(resBody, res); err != nil {
return nil, err
}
}
case http.StatusOK:
if err := json.Unmarshal(resBody, res); err != nil {
return nil, err
}
case http.StatusNoContent:
default:
errMessage := string(resBody)
var reasons []reason
if strings.Contains(string(resBody), "errorMsg") {
kperr := KPError{}
json.Unmarshal(resBody, &kperr)
if len(kperr.Resources) > 0 && len(kperr.Resources[0].Message) > 0 {
errMessage = kperr.Resources[0].Message
reasons = kperr.Resources[0].Reasons
}
}
return nil, &Error{
URL: response.Request.URL.String(),
StatusCode: response.StatusCode,
Message: errMessage,
BodyContent: resBody,
CorrelationID: corrID,
Reasons: reasons,
}
}
return response, nil
}
// getRetryableClient returns a fully configured retryable HTTP client
func getRetryableClient(client *http.Client) *rhttp.Client {
// build base client with the library defaults and override as neeeded
rc := rhttp.NewClient()
rc.Logger = nil
rc.HTTPClient = client
rc.RetryWaitMax = RetryWaitMax
rc.RetryMax = RetryMax
rc.CheckRetry = kpCheckRetry
rc.ErrorHandler = rhttp.PassthroughErrorHandler
return rc
}
// kpCheckRetry will retry on connection errors, server errors, and 429s (rate limit)
func kpCheckRetry(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()
}
if err != nil {
return true, err
}
// Retry on connection errors, 500+ errors (except 501 - not implemented), and 429 - too many requests
if resp.StatusCode == 0 || resp.StatusCode == 429 || (resp.StatusCode >= 500 && resp.StatusCode != 501) {
return true, nil
}
return false, nil
}
// ContextKey provides a type to auth context keys.
type ContextKey int
// NewContextWithAuth ...
func NewContextWithAuth(parent context.Context, auth string) context.Context {
return context.WithValue(parent, authContextKey, auth)
}
// getAccessToken returns the auth context from the given Context, or
// calls to the IAMTokenSource to retrieve an auth token.
func (c *Client) getAccessToken(ctx context.Context) (string, error) {
if ctx.Value(authContextKey) != nil {
return ctx.Value(authContextKey).(string), nil
}
if len(c.Config.Authorization) > 0 {
return c.Config.Authorization, nil
}
token, err := c.tokenSource.Token()
if err != nil {
return "", err
}
return fmt.Sprintf("%s %s", token.TokenType, token.AccessToken), nil
}
// getCorrelationId returns the correlation ID value from the given Context, or
// returns a new UUID if not present
func (c *Client) getCorrelationID(ctx context.Context) string {
corrID := GetCorrelationID(ctx)
if corrID == nil {
return uuid.New().String()
}
return corrID.String()
}
// NewContextWithCorrelationID retuns a context containing the UUID
func NewContextWithCorrelationID(ctx context.Context, uuid *uuid.UUID) context.Context {
return context.WithValue(ctx, cidCtxKey, uuid)
}
// GetCorrelationID returns the correlation ID from the context
func GetCorrelationID(ctx context.Context) *uuid.UUID {
if id := ctx.Value(cidCtxKey); id != nil {
return id.(*uuid.UUID)
}
return nil
}
func (c ctxKey) String() string {
return string(c)
}
// Logger writes when called.
type Logger interface {
Info(...interface{})
}
type logger struct {
writer func(...interface{})
}
func (l *logger) Info(args ...interface{}) {
l.writer(args...)
}
func NewLogger(writer func(...interface{})) Logger {
return &logger{writer: writer}
}
var dumpers = []Dump{dumpNone, dumpBodyOnly, dumpAll, dumpFailOnly, dumpAllNoRedact}
// Dump writes various parts of an HTTP request and an HTTP response.
type Dump func(*http.Request, *http.Response, []byte, []byte, Logger, []string)
// Redact replaces various pieces of output.
type Redact func(string, []string) string
// dumpFailOnly calls dumpAll when the HTTP response isn't 200 (ok),
// 201 (created), or 204 (no content).
func dumpFailOnly(req *http.Request, rsp *http.Response, reqBody, resBody []byte, log Logger, redactStrings []string) {
switch rsp.StatusCode {
case http.StatusOK, http.StatusCreated, http.StatusNoContent:
return
}
dumpAll(req, rsp, reqBody, resBody, log, redactStrings)
}
// dumpAll dumps the HTTP request and the HTTP response body.
func dumpAll(req *http.Request, rsp *http.Response, reqBody, resBody []byte, log Logger, redactStrings []string) {
dumpRequest(req, rsp, log, redactStrings, redact)
dumpBody(reqBody, resBody, log, redactStrings, redact)
}
// dumpAllNoRedact dumps the HTTP request and HTTP response body without redaction.
func dumpAllNoRedact(req *http.Request, rsp *http.Response, reqBody, resBody []byte, log Logger, redactStrings []string) {
dumpRequest(req, rsp, log, redactStrings, noredact)
dumpBody(reqBody, resBody, log, redactStrings, noredact)
}
// dumpBodyOnly dumps the HTTP response body.
func dumpBodyOnly(req *http.Request, rsp *http.Response, reqBody, resBody []byte, log Logger, redactStrings []string) {
dumpBody(reqBody, resBody, log, redactStrings, redact)
}
// dumpNone does nothing.
func dumpNone(req *http.Request, rsp *http.Response, reqBody, resBody []byte, log Logger, redactStrings []string) {
}
// dumpRequest dumps the HTTP request.
func dumpRequest(req *http.Request, rsp *http.Response, log Logger, redactStrings []string, redact Redact) {
// log.Info(redact(fmt.Sprint(req), redactStrings))
// log.Info(redact(fmt.Sprint(rsp), redactStrings))
}
// dumpBody dumps the HTTP response body with redactions.
func dumpBody(reqBody, resBody []byte, log Logger, redactStrings []string, redact Redact) {
// log.Info(string(redact(string(reqBody), redactStrings)))
// Redact the access token and refresh token if it shows up in the reponnse body. This will happen
// when using an API Key
var auth iam.Token
if strings.Contains(string(resBody), "access_token") {
err := json.Unmarshal(resBody, &auth)
if err != nil {
log.Info(err)
}
redactStrings = append(redactStrings, auth.AccessToken)
redactStrings = append(redactStrings, auth.RefreshToken)
}
// log.Info(string(redact(string(resBody), redactStrings)))
}
// redact replaces substrings within the given string.
func redact(s string, redactStrings []string) string {
if len(redactStrings) < 1 {
return s
}
var a []string
for _, s1 := range redactStrings {
if s1 != "" {
a = append(a, s1)
a = append(a, "***Value redacted***")
}
}
r := strings.NewReplacer(a...)
return r.Replace(s)
}
// noredact does not perform redaction, and returns the given string.
func noredact(s string, redactStrings []string) string {
return s
}

317
vendor/github.com/IBM/keyprotect-go-client/policy.go generated vendored Normal file
View File

@ -0,0 +1,317 @@
// Copyright 2020 IBM Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kp
import (
"context"
"fmt"
"net/url"
"time"
)
const (
policyType = "application/vnd.ibm.kms.policy+json"
RotationPolicy = "rotation"
)
// Policy represents a policy as returned by the KP API.
type Policy struct {
Type string `json:"type,omitempty"`
CreatedBy string `json:"createdBy,omitempty"`
CreatedAt *time.Time `json:"creationDate,omitempty"`
CRN string `json:"crn,omitempty"`
UpdatedAt *time.Time `json:"lastUpdateDate,omitempty"`
UpdatedBy string `json:"updatedBy,omitempty"`
Rotation *Rotation `json:"rotation,omitempty"`
DualAuth *DualAuth `json:"dualAuthDelete,omitempty"`
}
type Rotation struct {
Interval int `json:"interval_month,omitempty"`
}
type DualAuth struct {
Enabled *bool `json:"enabled,omitempty"`
}
// PoliciesMetadata represents the metadata of a collection of keys.
type PoliciesMetadata struct {
CollectionType string `json:"collectionType"`
NumberOfPolicies int `json:"collectionTotal"`
}
// Policies represents a collection of Policies.
type Policies struct {
Metadata PoliciesMetadata `json:"metadata"`
Policies []Policy `json:"resources"`
}
// GetPolicy retrieves a policy by Key ID. This function is
// deprecated, as it only returns one policy and does not let you
// select which policy set it will return. It is kept for backward
// compatibility on keys with only one rotation policy. Please update
// to use the new GetPolicies or Get<type>Policy functions.
func (c *Client) GetPolicy(ctx context.Context, id string) (*Policy, error) {
policyresponse := Policies{}
req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", id), nil)
if err != nil {
return nil, err
}
_, err = c.do(ctx, req, &policyresponse)
if err != nil {
return nil, err
}
return &policyresponse.Policies[0], nil
}
// SetPolicy updates a policy resource by specifying the ID of the key and
// 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
// 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) {
policy := Policy{
Type: policyType,
Rotation: &Rotation{
Interval: rotationInterval,
},
}
policyRequest := Policies{
Metadata: PoliciesMetadata{
CollectionType: policyType,
NumberOfPolicies: 1,
},
Policies: []Policy{policy},
}
policyresponse := Policies{}
req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", id), &policyRequest)
if err != nil {
return nil, err
}
req.Header.Set("Prefer", preferHeaders[prefer])
_, err = c.do(ctx, req, &policyresponse)
if err != nil {
return nil, err
}
return &policyresponse.Policies[0], nil
}
// GetPolicies retrieves all policies details associated with a Key ID.
func (c *Client) GetPolicies(ctx context.Context, id string) ([]Policy, error) {
policyresponse := Policies{}
req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", id), nil)
if err != nil {
return nil, err
}
_, err = c.do(ctx, req, &policyresponse)
if err != nil {
return nil, err
}
return policyresponse.Policies, nil
}
func (c *Client) getPolicy(ctx context.Context, id, policyType string, policyresponse *Policies) error {
req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", id), nil)
if err != nil {
return err
}
v := url.Values{}
v.Set("policy", policyType)
req.URL.RawQuery = v.Encode()
_, err = c.do(ctx, req, &policyresponse)
if err != nil {
return err
}
return err
}
// GetRotationPolivy method retrieves rotation policy details of a key
// 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
func (c *Client) GetRotationPolicy(ctx context.Context, id string) (*Policy, error) {
policyresponse := Policies{}
err := c.getPolicy(ctx, id, RotationPolicy, &policyresponse)
if err != nil {
return nil, err
}
if len(policyresponse.Policies) == 0 {
return nil, nil
}
return &policyresponse.Policies[0], nil
}
// GetDualAuthDeletePolicy method retrieves dual auth delete policy details of a key
// 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
func (c *Client) GetDualAuthDeletePolicy(ctx context.Context, id string) (*Policy, error) {
policyresponse := Policies{}
err := c.getPolicy(ctx, id, DualAuthDelete, &policyresponse)
if err != nil {
return nil, err
}
if len(policyresponse.Policies) == 0 {
return nil, nil
}
return &policyresponse.Policies[0], nil
}
func (c *Client) setPolicy(ctx context.Context, id, policyType string, policyRequest Policies) (*Policies, error) {
policyresponse := Policies{}
req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", id), &policyRequest)
if err != nil {
return nil, err
}
v := url.Values{}
v.Set("policy", policyType)
req.URL.RawQuery = v.Encode()
_, err = c.do(ctx, req, &policyresponse)
if err != nil {
return nil, err
}
return &policyresponse, nil
}
// SetRotationPolicy updates the rotation policy associated with a key by specifying key ID and rotation interval.
// 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
func (c *Client) SetRotationPolicy(ctx context.Context, id string, rotationInterval int) (*Policy, error) {
policy := Policy{
Type: policyType,
Rotation: &Rotation{
Interval: rotationInterval,
},
}
policyRequest := Policies{
Metadata: PoliciesMetadata{
CollectionType: policyType,
NumberOfPolicies: 1,
},
Policies: []Policy{policy},
}
policyresponse, err := c.setPolicy(ctx, id, RotationPolicy, policyRequest)
if err != nil {
return nil, err
}
if len(policyresponse.Policies) == 0 {
return nil, nil
}
return &policyresponse.Policies[0], nil
}
// SetDualAuthDeletePolicy updates the dual auth delete policy by passing the key ID and enable detail
// 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
func (c *Client) SetDualAuthDeletePolicy(ctx context.Context, id string, enabled bool) (*Policy, error) {
policy := Policy{
Type: policyType,
DualAuth: &DualAuth{
Enabled: &enabled,
},
}
policyRequest := Policies{
Metadata: PoliciesMetadata{
CollectionType: policyType,
NumberOfPolicies: 1,
},
Policies: []Policy{policy},
}
policyresponse, err := c.setPolicy(ctx, id, DualAuthDelete, policyRequest)
if err != nil {
return nil, err
}
if len(policyresponse.Policies) == 0 {
return nil, nil
}
return &policyresponse.Policies[0], nil
}
// SetPolicies updates all policies of the key or a single policy by passing key ID.
// 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.
// 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) {
policies := []Policy{}
if setRotationPolicy {
rotationPolicy := Policy{
Type: policyType,
Rotation: &Rotation{
Interval: rotationInterval,
},
}
policies = append(policies, rotationPolicy)
}
if setDualAuthDeletePolicy {
dulaAuthPolicy := Policy{
Type: policyType,
DualAuth: &DualAuth{
Enabled: &dualAuthEnable,
},
}
policies = append(policies, dulaAuthPolicy)
}
policyRequest := Policies{
Metadata: PoliciesMetadata{
CollectionType: policyType,
NumberOfPolicies: len(policies),
},
Policies: policies,
}
policyresponse := Policies{}
req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", id), &policyRequest)
if err != nil {
return nil, err
}
_, err = c.do(ctx, req, &policyresponse)
if err != nil {
return nil, err
}
return policyresponse.Policies, nil
}

View File

@ -0,0 +1,69 @@
// Copyright 2020 IBM Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kp
import (
"context"
"fmt"
"net/url"
"time"
)
// Registration represents the registration as returned by KP API
type Registration struct {
KeyID string `json:"keyId,omitempty"`
ResourceCrn string `json:"resourceCrn,omitempty"`
CreatedBy string `json:"createdBy,omitempty"`
CreationDate *time.Time `json:"creationDate,omitempty"`
UpdatedBy string `json:"updatedBy,omitempty"`
LastUpdateDate *time.Time `json:"lastUpdated,omitempty"`
Description string `json:"description,omitempty"`
PreventKeyDeletion bool `json:"preventKeyDeletion,omitempty"`
KeyVersion KeyVersion `json:"keyVersion,omitempty"`
}
type registrations struct {
Metadata KeysMetadata `json:"metadata"`
Registrations []Registration `json:"resources"`
}
// ListRegistrations retrieves a collection of registrations
func (c *Client) ListRegistrations(ctx context.Context, keyId, crn string) (*registrations, error) {
registrationAPI := ""
if keyId != "" {
registrationAPI = fmt.Sprintf("keys/%s/registrations", keyId)
} else {
registrationAPI = "keys/registrations"
}
req, err := c.newRequest("GET", registrationAPI, nil)
if err != nil {
return nil, err
}
if crn != "" {
v := url.Values{}
v.Set("urlEncodedResourceCRNQuery", crn)
req.URL.RawQuery = v.Encode()
}
regs := registrations{}
_, err = c.do(ctx, req, &regs)
if err != nil {
return nil, err
}
return &regs, nil
}

4
vendor/modules.txt vendored
View File

@ -1,3 +1,7 @@
# github.com/IBM/keyprotect-go-client v0.7.0
## explicit; go 1.12
github.com/IBM/keyprotect-go-client
github.com/IBM/keyprotect-go-client/iam
# github.com/armon/go-metrics v0.3.9
## explicit; go 1.12
github.com/armon/go-metrics