Merge remote-tracking branch 'origin/main' into llb-byow

This commit is contained in:
Jeff Mitchell committed 2022-06-15 16:45:00 -05:00
commit 704d68848c
160 files changed
+15933 -4684

No files matched your search

+26 -2
View File
@@ -6,6 +6,19 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
### New and Improved
* Credentials: This release introduces a new credential store type `static`,
which simply takes in a user-supplied credential and stores it (encrypted)
directly in Boundary. Currently, the `static` credential store can hold
credentials of type `username_password`. These credentials can act as
credential sources for targets, similar to credential libraries from the
`vault` credential store, and thus can be brokered to users at session
authorization time. [PR](https://github.com/hashicorp/boundary/pull/2174)
* `boundary connect` Credential Brokering Integration: we have extended integration
into the `boundary connect` helpers. A new `sshpass` style has been added to the
`ssh` helper, when used, if the credential contains a username/password and `sshpass`
is installed, the command will automatically pass the credentials to the `ssh` process.
Additionally, the default `ssh` helper will now use the `username` of the brokered credential.
[PR](https://github.com/hashicorp/boundary/pull/2191).
* controller: Improve response time for listing sessions.
This also creates a new periodic job that will delete terminated
sessions after 1 hour.
@@ -14,8 +27,21 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
* event filtering: Change event filters to use lowercase and snake case for data
elements like the rest of Boundary filters do.
### Bug Fixes
* The plugin execution_dir configuration parameter is now respected.
[PR](https://github.com/hashicorp/boundary/pull/2183).
### Deprecations/Changes
* Targets: Removes support for `credential libraries` with respect to Target resources.
The `library` `fields` and `actions` were deprecated in [Boundary 0.5.0](#050-20210802),
please use `credential sources` instead. See changelog referenced above for
more details ([PR](https://github.com/hashicorp/boundary/pull/1533)).
* Credential Libraries: The `user_password` credential type has been renamed to
`username_password` to remove any inconsistency over what the credential type is.
All existing `user_password` typed credential libraries will be migrated to
`username_password` ([PR](https://github.com/hashicorp/boundary/pull/2154)).
* controller: Change the default behavior of the session list endpoint
to no longer include sessions in a terminated state and introduces
a new query parameter/cli flag to include the terminated sessions.
@@ -30,8 +56,6 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
documentation](https://www.boundaryproject.io/docs/concepts/security/permissions/assignable-permissions)
for more details.
### Bug Fixes
## 0.8.1 (2022/05/13)
### Bug Fixes
+3
View File
@@ -159,6 +159,7 @@ protobuild:
@protoc-go-inject-tag -input=./internal/scheduler/job/store/job.pb.go
@protoc-go-inject-tag -input=./internal/credential/store/credential.pb.go
@protoc-go-inject-tag -input=./internal/credential/vault/store/vault.pb.go
@protoc-go-inject-tag -input=./internal/credential/static/store/static.pb.go
@protoc-go-inject-tag -input=./internal/servers/servers.pb.go
@protoc-go-inject-tag -input=./internal/kms/store/audit_key.pb.go
@@ -188,6 +189,8 @@ protobuild:
@protoc-go-inject-tag -input=./internal/gen/controller/api/services/credential_store_service.pb.go
@protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/credentiallibraries/credential_library.pb.go
@protoc-go-inject-tag -input=./internal/gen/controller/api/services/credential_library_service.pb.go
@protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/credentials/credential.pb.go
@protoc-go-inject-tag -input=./internal/gen/controller/api/services/credential_service.pb.go
@protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/roles/role.pb.go
@protoc-go-inject-tag -input=./internal/gen/controller/api/services/role_service.pb.go
@protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/sessions/session.pb.go
+325
View File
@@ -0,0 +1,325 @@
// Code generated by "make api"; DO NOT EDIT.
package credentials
import (
"context"
"errors"
"fmt"
"net/url"
"time"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/scopes"
)
type Credential struct {
Id string `json:"id,omitempty"`
CredentialStoreId string `json:"credential_store_id,omitempty"`
Scope *scopes.ScopeInfo `json:"scope,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
CreatedTime time.Time `json:"created_time,omitempty"`
UpdatedTime time.Time `json:"updated_time,omitempty"`
Version uint32 `json:"version,omitempty"`
Type string `json:"type,omitempty"`
Attributes map[string]interface{} `json:"attributes,omitempty"`
AuthorizedActions []string `json:"authorized_actions,omitempty"`
response *api.Response
}
type CredentialReadResult struct {
Item *Credential
response *api.Response
}
func (n CredentialReadResult) GetItem() interface{} {
return n.Item
}
func (n CredentialReadResult) GetResponse() *api.Response {
return n.response
}
type CredentialCreateResult = CredentialReadResult
type CredentialUpdateResult = CredentialReadResult
type CredentialDeleteResult struct {
response *api.Response
}
// GetItem will always be nil for CredentialDeleteResult
func (n CredentialDeleteResult) GetItem() interface{} {
return nil
}
func (n CredentialDeleteResult) GetResponse() *api.Response {
return n.response
}
type CredentialListResult struct {
Items []*Credential
response *api.Response
}
func (n CredentialListResult) GetItems() interface{} {
return n.Items
}
func (n CredentialListResult) GetResponse() *api.Response {
return n.response
}
// Client is a client for this collection
type Client struct {
client *api.Client
}
// Creates a new client for this collection. The submitted API client is cloned;
// modifications to it after generating this client will not have effect. If you
// need to make changes to the underlying API client, use ApiClient() to access
// it.
func NewClient(c *api.Client) *Client {
return &Client{client: c.Clone()}
}
// ApiClient returns the underlying API client
func (c *Client) ApiClient() *api.Client {
return c.client
}
func (c *Client) Create(ctx context.Context, resourceType string, credentialStoreId string, opt ...Option) (*CredentialCreateResult, error) {
if credentialStoreId == "" {
return nil, fmt.Errorf("empty credentialStoreId value passed into Create request")
}
opts, apiOpts := getOpts(opt...)
if c.client == nil {
return nil, fmt.Errorf("nil client")
}
if resourceType == "" {
return nil, fmt.Errorf("empty resourceType value passed into Create request")
} else {
opts.postMap["type"] = resourceType
}
opts.postMap["credential_store_id"] = credentialStoreId
req, err := c.client.NewRequest(ctx, "POST", "credentials", opts.postMap, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating Create request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during Create call: %w", err)
}
target := new(CredentialCreateResult)
target.Item = new(Credential)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding Create response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) Read(ctx context.Context, id string, opt ...Option) (*CredentialReadResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into Read request")
}
if c.client == nil {
return nil, fmt.Errorf("nil client")
}
opts, apiOpts := getOpts(opt...)
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("credentials/%s", url.PathEscape(id)), nil, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating Read request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error performing client request during Read call: %w", err)
}
target := new(CredentialReadResult)
target.Item = new(Credential)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding Read response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) Update(ctx context.Context, id string, version uint32, opt ...Option) (*CredentialUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into Update request")
}
if c.client == nil {
return nil, fmt.Errorf("nil client")
}
opts, apiOpts := getOpts(opt...)
if version == 0 {
if !opts.withAutomaticVersioning {
return nil, errors.New("zero version number passed into Update request and automatic versioning not specified")
}
existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...)
if existingErr != nil {
if api.AsServerError(existingErr) != nil {
return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr)
}
return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr)
}
if existingTarget == nil {
return nil, errors.New("nil resource response found when performing initial check-and-set read")
}
if existingTarget.Item == nil {
return nil, errors.New("nil resource found when performing initial check-and-set read")
}
version = existingTarget.Item.Version
}
opts.postMap["version"] = version
req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("credentials/%s", url.PathEscape(id)), opts.postMap, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating Update request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during Update call: %w", err)
}
target := new(CredentialUpdateResult)
target.Item = new(Credential)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding Update response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) Delete(ctx context.Context, id string, opt ...Option) (*CredentialDeleteResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into Delete request")
}
if c.client == nil {
return nil, fmt.Errorf("nil client")
}
opts, apiOpts := getOpts(opt...)
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("credentials/%s", url.PathEscape(id)), nil, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating Delete request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during Delete call: %w", err)
}
apiErr, err := resp.Decode(nil)
if err != nil {
return nil, fmt.Errorf("error decoding Delete response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target := &CredentialDeleteResult{
response: resp,
}
return target, nil
}
func (c *Client) List(ctx context.Context, credentialStoreId string, opt ...Option) (*CredentialListResult, error) {
if credentialStoreId == "" {
return nil, fmt.Errorf("empty credentialStoreId value passed into List request")
}
if c.client == nil {
return nil, fmt.Errorf("nil client")
}
opts, apiOpts := getOpts(opt...)
opts.queryMap["credential_store_id"] = credentialStoreId
req, err := c.client.NewRequest(ctx, "GET", "credentials", nil, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating List request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during List call: %w", err)
}
target := new(CredentialListResult)
apiErr, err := resp.Decode(target)
if err != nil {
return nil, fmt.Errorf("error decoding List response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
+135
View File
@@ -0,0 +1,135 @@
package credentials
import (
"strings"
"github.com/hashicorp/boundary/api"
)
// Option is a func that sets optional attributes for a call. This does not need
// to be used directly, but instead option arguments are built from the
// functions in this package. WithX options set a value to that given in the
// argument; DefaultX options indicate that the value should be set to its
// default. When an API call is made options are processed in ther order they
// appear in the function call, so for a given argument X, a succession of WithX
// or DefaultX calls will result in the last call taking effect.
type Option func(*options)
type options struct {
postMap map[string]interface{}
queryMap map[string]string
withAutomaticVersioning bool
withSkipCurlOutput bool
withFilter string
}
func getDefaultOptions() options {
return options{
postMap: make(map[string]interface{}),
queryMap: make(map[string]string),
}
}
func getOpts(opt ...Option) (options, []api.Option) {
opts := getDefaultOptions()
for _, o := range opt {
if o != nil {
o(&opts)
}
}
var apiOpts []api.Option
if opts.withSkipCurlOutput {
apiOpts = append(apiOpts, api.WithSkipCurlOutput(true))
}
if opts.withFilter != "" {
opts.queryMap["filter"] = opts.withFilter
}
return opts, apiOpts
}
// If set, and if the version is zero during an update, the API will perform a
// fetch to get the current version of the resource and populate it during the
// update call. This is convenient but opens up the possibility for subtle
// order-of-modification issues, so use carefully.
func WithAutomaticVersioning(enable bool) Option {
return func(o *options) {
o.withAutomaticVersioning = enable
}
}
// WithSkipCurlOutput tells the API to not use the current call for cURL output.
// Useful for when we need to look up versions.
func WithSkipCurlOutput(skip bool) Option {
return func(o *options) {
o.withSkipCurlOutput = true
}
}
// WithFilter tells the API to filter the items returned using the provided
// filter term. The filter should be in a format supported by
// hashicorp/go-bexpr.
func WithFilter(filter string) Option {
return func(o *options) {
o.withFilter = strings.TrimSpace(filter)
}
}
func WithAttributes(inAttributes map[string]interface{}) Option {
return func(o *options) {
o.postMap["attributes"] = inAttributes
}
}
func DefaultAttributes() Option {
return func(o *options) {
o.postMap["attributes"] = nil
}
}
func WithDescription(inDescription string) Option {
return func(o *options) {
o.postMap["description"] = inDescription
}
}
func DefaultDescription() Option {
return func(o *options) {
o.postMap["description"] = nil
}
}
func WithName(inName string) Option {
return func(o *options) {
o.postMap["name"] = inName
}
}
func DefaultName() Option {
return func(o *options) {
o.postMap["name"] = nil
}
}
func WithUsernamePasswordCredentialPassword(inPassword string) Option {
return func(o *options) {
raw, ok := o.postMap["attributes"]
if !ok {
raw = interface{}(map[string]interface{}{})
}
val := raw.(map[string]interface{})
val["password"] = inPassword
o.postMap["attributes"] = val
}
}
func WithUsernamePasswordCredentialUsername(inUsername string) Option {
return func(o *options) {
raw, ok := o.postMap["attributes"]
if !ok {
raw = interface{}(map[string]interface{}{})
}
val := raw.(map[string]interface{})
val["username"] = inUsername
o.postMap["attributes"] = val
}
}
@@ -0,0 +1,8 @@
// Code generated by "make api"; DO NOT EDIT.
package credentials
type UsernamePasswordAttributes struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
PasswordHmac string `json:"password_hmac,omitempty"`
}
-10
View File
@@ -1,10 +0,0 @@
// Code generated by "make api"; DO NOT EDIT.
package targets
type CredentialLibrary struct {
Id string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
CredentialStoreId string `json:"credential_store_id,omitempty"`
Type string `json:"type,omitempty"`
}
-12
View File
@@ -87,18 +87,6 @@ func WithRecursive(recurse bool) Option {
}
}
func WithApplicationCredentialLibraryIds(inApplicationCredentialLibraryIds []string) Option {
return func(o *options) {
o.postMap["application_credential_library_ids"] = inApplicationCredentialLibraryIds
}
}
func DefaultApplicationCredentialLibraryIds() Option {
return func(o *options) {
o.postMap["application_credential_library_ids"] = nil
}
}
func WithApplicationCredentialSourceIds(inApplicationCredentialSourceIds []string) Option {
return func(o *options) {
o.postMap["application_credential_source_ids"] = inApplicationCredentialSourceIds
+3 -4
View File
@@ -2,8 +2,7 @@
package targets
type SessionCredential struct {
CredentialSource *CredentialSource `json:"credential_source,omitempty"`
CredentialLibrary *CredentialLibrary `json:"credential_library,omitempty"`
Secret *SessionSecret `json:"secret,omitempty"`
Credential map[string]interface{} `json:"credential,omitempty"`
CredentialSource *CredentialSource `json:"credential_source,omitempty"`
Secret *SessionSecret `json:"secret,omitempty"`
Credential map[string]interface{} `json:"credential,omitempty"`
}
+22 -216
View File
@@ -13,30 +13,28 @@ import (
)
type Target struct {
Id string `json:"id,omitempty"`
ScopeId string `json:"scope_id,omitempty"`
Scope *scopes.ScopeInfo `json:"scope,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
CreatedTime time.Time `json:"created_time,omitempty"`
UpdatedTime time.Time `json:"updated_time,omitempty"`
Version uint32 `json:"version,omitempty"`
Type string `json:"type,omitempty"`
HostSetIds []string `json:"host_set_ids,omitempty"`
HostSets []*HostSet `json:"host_sets,omitempty"`
HostSourceIds []string `json:"host_source_ids,omitempty"`
HostSources []*HostSource `json:"host_sources,omitempty"`
SessionMaxSeconds uint32 `json:"session_max_seconds,omitempty"`
SessionConnectionLimit int32 `json:"session_connection_limit,omitempty"`
WorkerFilter string `json:"worker_filter,omitempty"`
ApplicationCredentialLibraryIds []string `json:"application_credential_library_ids,omitempty"`
ApplicationCredentialLibraries []*CredentialLibrary `json:"application_credential_libraries,omitempty"`
ApplicationCredentialSourceIds []string `json:"application_credential_source_ids,omitempty"`
ApplicationCredentialSources []*CredentialSource `json:"application_credential_sources,omitempty"`
EgressCredentialSourceIds []string `json:"egress_credential_source_ids,omitempty"`
EgressCredentialSources []*CredentialSource `json:"egress_credential_sources,omitempty"`
Attributes map[string]interface{} `json:"attributes,omitempty"`
AuthorizedActions []string `json:"authorized_actions,omitempty"`
Id string `json:"id,omitempty"`
ScopeId string `json:"scope_id,omitempty"`
Scope *scopes.ScopeInfo `json:"scope,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
CreatedTime time.Time `json:"created_time,omitempty"`
UpdatedTime time.Time `json:"updated_time,omitempty"`
Version uint32 `json:"version,omitempty"`
Type string `json:"type,omitempty"`
HostSetIds []string `json:"host_set_ids,omitempty"`
HostSets []*HostSet `json:"host_sets,omitempty"`
HostSourceIds []string `json:"host_source_ids,omitempty"`
HostSources []*HostSource `json:"host_sources,omitempty"`
SessionMaxSeconds uint32 `json:"session_max_seconds,omitempty"`
SessionConnectionLimit int32 `json:"session_connection_limit,omitempty"`
WorkerFilter string `json:"worker_filter,omitempty"`
ApplicationCredentialSourceIds []string `json:"application_credential_source_ids,omitempty"`
ApplicationCredentialSources []*CredentialSource `json:"application_credential_sources,omitempty"`
EgressCredentialSourceIds []string `json:"egress_credential_source_ids,omitempty"`
EgressCredentialSources []*CredentialSource `json:"egress_credential_sources,omitempty"`
Attributes map[string]interface{} `json:"attributes,omitempty"`
AuthorizedActions []string `json:"authorized_actions,omitempty"`
response *api.Response
}
@@ -337,70 +335,6 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Targ
return target, nil
}
func (c *Client) AddCredentialLibraries(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into AddCredentialLibraries request")
}
if c.client == nil {
return nil, errors.New("nil client")
}
opts, apiOpts := getOpts(opt...)
if version == 0 {
if !opts.withAutomaticVersioning {
return nil, errors.New("zero version number passed into AddCredentialLibraries request")
}
existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...)
if existingErr != nil {
if api.AsServerError(existingErr) != nil {
return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr)
}
return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr)
}
if existingTarget == nil {
return nil, errors.New("nil resource response found when performing initial check-and-set read")
}
if existingTarget.Item == nil {
return nil, errors.New("nil resource found when performing initial check-and-set read")
}
version = existingTarget.Item.Version
}
opts.postMap["version"] = version
req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("targets/%s:add-credential-libraries", url.PathEscape(id)), opts.postMap, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating AddCredentialLibraries request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during AddCredentialLibraries call: %w", err)
}
target := new(TargetUpdateResult)
target.Item = new(Target)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding AddCredentialLibraries response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) AddCredentialSources(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into AddCredentialSources request")
@@ -605,70 +539,6 @@ func (c *Client) AddHostSources(ctx context.Context, id string, version uint32,
return target, nil
}
func (c *Client) SetCredentialLibraries(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into SetCredentialLibraries request")
}
if c.client == nil {
return nil, errors.New("nil client")
}
opts, apiOpts := getOpts(opt...)
if version == 0 {
if !opts.withAutomaticVersioning {
return nil, errors.New("zero version number passed into SetCredentialLibraries request")
}
existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...)
if existingErr != nil {
if api.AsServerError(existingErr) != nil {
return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr)
}
return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr)
}
if existingTarget == nil {
return nil, errors.New("nil resource response found when performing initial check-and-set read")
}
if existingTarget.Item == nil {
return nil, errors.New("nil resource found when performing initial check-and-set read")
}
version = existingTarget.Item.Version
}
opts.postMap["version"] = version
req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("targets/%s:set-credential-libraries", url.PathEscape(id)), opts.postMap, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating SetCredentialLibraries request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during SetCredentialLibraries call: %w", err)
}
target := new(TargetUpdateResult)
target.Item = new(Target)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding SetCredentialLibraries response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) SetCredentialSources(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into SetCredentialSources request")
@@ -865,70 +735,6 @@ func (c *Client) SetHostSources(ctx context.Context, id string, version uint32,
return target, nil
}
func (c *Client) RemoveCredentialLibraries(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into RemoveCredentialLibraries request")
}
if c.client == nil {
return nil, errors.New("nil client")
}
opts, apiOpts := getOpts(opt...)
if version == 0 {
if !opts.withAutomaticVersioning {
return nil, errors.New("zero version number passed into RemoveCredentialLibraries request")
}
existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...)
if existingErr != nil {
if api.AsServerError(existingErr) != nil {
return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr)
}
return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr)
}
if existingTarget == nil {
return nil, errors.New("nil resource response found when performing initial check-and-set read")
}
if existingTarget.Item == nil {
return nil, errors.New("nil resource found when performing initial check-and-set read")
}
version = existingTarget.Item.Version
}
opts.postMap["version"] = version
req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("targets/%s:remove-credential-libraries", url.PathEscape(id)), opts.postMap, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating RemoveCredentialLibraries request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during RemoveCredentialLibraries call: %w", err)
}
target := new(TargetUpdateResult)
target.Item = new(Target)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding RemoveCredentialLibraries response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) RemoveCredentialSources(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into RemoveCredentialSources request")
+19 -56
View File
@@ -8,6 +8,7 @@ import (
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authmethods"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authtokens"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentialstores"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/groups"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
@@ -453,15 +454,24 @@ var inputStructs = []*structInfo{
versionEnabled: true,
createResponseTypes: true,
},
// Credentials
{
inProto: &credentialstores.VaultCredentialStoreAttributes{},
outFile: "credentialstores/vault_credential_store_attributes.gen.go",
subtypeName: "VaultCredentialStore",
inProto: &credentials.UsernamePasswordAttributes{},
outFile: "credentials/username_password_attributes.gen.go",
subtypeName: "UsernamePasswordCredential",
fieldOverrides: []fieldInfo{
{
Name: "Username",
SkipDefault: true,
},
{
Name: "Password",
SkipDefault: true,
},
},
},
{
inProto: &credentialstores.CredentialStore{},
outFile: "credentialstores/credential_store.gen.go",
inProto: &credentials.Credential{},
outFile: "credentials/credential.gen.go",
templates: []*template.Template{
clientTemplate,
commonCreateTemplate,
@@ -470,8 +480,6 @@ var inputStructs = []*structInfo{
deleteTemplate,
listTemplate,
},
pluralResourceName: "credential-stores",
parentTypeName: "scope",
extraRequiredParams: []requiredParam{
{
Name: "resourceType",
@@ -479,47 +487,12 @@ var inputStructs = []*structInfo{
PostType: "type",
},
},
versionEnabled: true,
createResponseTypes: true,
recursiveListing: true,
fieldOverrides: []fieldInfo{
{
Name: "Address",
SkipDefault: true,
},
{
Name: "Token",
SkipDefault: true,
},
},
},
{
inProto: &credentiallibraries.VaultCredentialLibraryAttributes{},
outFile: "credentiallibraries/vault_credential_library_attributes.gen.go",
subtypeName: "VaultCredentialLibrary",
fieldOverrides: []fieldInfo{
{
Name: "Path",
SkipDefault: true,
},
},
},
{
inProto: &credentiallibraries.CredentialLibrary{},
outFile: "credentiallibraries/credential_library.gen.go",
templates: []*template.Template{
clientTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
listTemplate,
},
pluralResourceName: "credential-libraries",
pluralResourceName: "credentials",
parentTypeName: "credential-store",
versionEnabled: true,
createResponseTypes: true,
},
// Host related resources
{
inProto: &hostcatalogs.HostCatalog{},
@@ -610,10 +583,6 @@ var inputStructs = []*structInfo{
inProto: &targets.HostSource{},
outFile: "targets/host_source.gen.go",
},
{
inProto: &targets.CredentialLibrary{},
outFile: "targets/credential_library.gen.go",
},
{
inProto: &targets.CredentialSource{},
outFile: "targets/credential_source.gen.go",
@@ -668,8 +637,7 @@ var inputStructs = []*structInfo{
SliceType: "[]string",
VarName: "hostSourceIds",
},
"CredentialLibraries": {},
"CredentialSources": {},
"CredentialSources": {},
},
extraFields: []fieldInfo{
{
@@ -690,11 +658,6 @@ var inputStructs = []*structInfo{
FieldType: "string",
SkipDefault: true,
},
{
Name: "ApplicationCredentialLibraryIds",
ProtoName: "application_credential_library_ids",
FieldType: "[]string",
},
{
Name: "ApplicationCredentialSourceIds",
ProtoName: "application_credential_source_ids",
+1
View File
@@ -589,6 +589,7 @@ func (b *Server) SetupKMSes(ctx context.Context, ui cli.Ui, config *config.Confi
configutil.WithPluginOptions(
pluginutil.WithPluginsMap(kms_plugin_assets.BuiltinKmsPlugins()),
pluginutil.WithPluginsFilesystem(kms_plugin_assets.KmsPluginPrefix, kms_plugin_assets.FileSystem()),
pluginutil.WithPluginExecutionDirectory(config.Plugins.ExecutionDir),
),
configutil.WithLogger(pluginLogger.Named(kms.Type).With("purpose", purpose)),
)
+61 -18
View File
@@ -9,6 +9,7 @@ import (
"github.com/hashicorp/boundary/internal/cmd/commands/config"
"github.com/hashicorp/boundary/internal/cmd/commands/connect"
"github.com/hashicorp/boundary/internal/cmd/commands/credentiallibrariescmd"
"github.com/hashicorp/boundary/internal/cmd/commands/credentialscmd"
"github.com/hashicorp/boundary/internal/cmd/commands/credentialstorescmd"
"github.com/hashicorp/boundary/internal/cmd/commands/database"
"github.com/hashicorp/boundary/internal/cmd/commands/dev"
@@ -411,6 +412,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
Func: "create",
}, nil
},
"credential-stores create static": func() (cli.Command, error) {
return &credentialstorescmd.StaticCommand{
Command: base.NewCommand(ui),
Func: "create",
}, nil
},
"credential-stores update": func() (cli.Command, error) {
return &credentialstorescmd.Command{
Command: base.NewCommand(ui),
@@ -423,6 +430,60 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
Func: "update",
}, nil
},
"credential-stores update static": func() (cli.Command, error) {
return &credentialstorescmd.StaticCommand{
Command: base.NewCommand(ui),
Func: "update",
}, nil
},
"credentials": func() (cli.Command, error) {
return &credentialscmd.Command{
Command: base.NewCommand(ui),
}, nil
},
"credentials read": func() (cli.Command, error) {
return &credentialscmd.Command{
Command: base.NewCommand(ui),
Func: "read",
}, nil
},
"credentials delete": func() (cli.Command, error) {
return &credentialscmd.Command{
Command: base.NewCommand(ui),
Func: "delete",
}, nil
},
"credentials list": func() (cli.Command, error) {
return &credentialscmd.Command{
Command: base.NewCommand(ui),
Func: "list",
}, nil
},
"credentials create": func() (cli.Command, error) {
return &credentialscmd.Command{
Command: base.NewCommand(ui),
Func: "create",
}, nil
},
"credentials create username-password": func() (cli.Command, error) {
return &credentialscmd.UsernamePasswordCommand{
Command: base.NewCommand(ui),
Func: "create",
}, nil
},
"credentials update": func() (cli.Command, error) {
return &credentialscmd.Command{
Command: base.NewCommand(ui),
Func: "update",
}, nil
},
"credentials update username-password": func() (cli.Command, error) {
return &credentialscmd.UsernamePasswordCommand{
Command: base.NewCommand(ui),
Func: "update",
}, nil
},
"groups": func() (cli.Command, error) {
return &groupscmd.Command{
@@ -939,24 +1000,6 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
Func: "set-host-sources",
}, nil
},
"targets add-credential-libraries": func() (cli.Command, error) {
return &targetscmd.Command{
Command: base.NewCommand(ui),
Func: "add-credential-libraries",
}, nil
},
"targets remove-credential-libraries": func() (cli.Command, error) {
return &targetscmd.Command{
Command: base.NewCommand(ui),
Func: "remove-credential-libraries",
}, nil
},
"targets set-credential-libraries": func() (cli.Command, error) {
return &targetscmd.Command{
Command: base.NewCommand(ui),
Func: "set-credential-libraries",
}, nil
},
"targets add-credential-sources": func() (cli.Command, error) {
return &targetscmd.Command{
Command: base.NewCommand(ui),
+16 -1
View File
@@ -492,6 +492,15 @@ func (c *Command) Run(args []string) (retCode int) {
// Credentials are brokered when connecting to the postgres db.
// TODO: Figure out how to handle cases where we don't automatically know how to
// broker the credentials like unrecognized or multiple credentials.
case "ssh":
if c.flagExec == "sshpass" {
// If we can broker ssh credentials using sshpass, skip displaying creds on client side.
// TODO: Figure out how to handle multiple credentials
break
}
// We cannot broker credentials print to user
fallthrough
case "connect":
// "connect" indicates there is no subcommand to the connect function.
// The only way a user will be able to connect to the session is by
@@ -838,7 +847,13 @@ func (c *Command) handleExec(passthroughArgs []string) {
args = append(args, c.rdpFlags.buildArgs(c, port, ip, addr)...)
case "ssh":
args = append(args, c.sshFlags.buildArgs(c, port, ip, addr)...)
sshArgs, sshEnvs, sshErr := c.sshFlags.buildArgs(c, port, ip, addr)
if sshErr != nil {
argsErr = sshErr
break
}
args = append(args, sshArgs...)
envs = append(envs, sshEnvs...)
case "kube":
kubeArgs, err := c.kubeFlags.buildArgs(c, port, ip, addr)
@@ -0,0 +1,61 @@
package connect
import (
"errors"
"github.com/hashicorp/boundary/api/targets"
"github.com/hashicorp/boundary/internal/credential"
"github.com/mitchellh/mapstructure"
)
type usernamePasswordCredentials struct {
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
}
// TODO: this should support multiple types of credentials and return an interface instead
// of only usernamepassword. Each subcommand can use the returned credentials as it needs.
func parseCredentials(creds []*targets.SessionCredential) ([]usernamePasswordCredentials, error) {
if creds == nil {
return nil, nil
}
var out []usernamePasswordCredentials
for _, cred := range creds {
if cred.CredentialSource == nil {
return nil, errors.New("missing credential source")
}
var c usernamePasswordCredentials
if cred.CredentialSource.CredentialType == string(credential.UsernamePasswordType) {
// Decode attributes from credential struct
if err := mapstructure.Decode(cred.Credential, &c); err != nil {
return nil, err
}
if c.Username != "" && c.Password != "" {
out = append(out, c)
continue
}
}
// Credential type is unspecified, make a best effort attempt to parse both username
// and password from Decoded field if it exists
if cred.Secret == nil || cred.Secret.Decoded == nil {
// No secret data continue to next credential
continue
}
switch cred.CredentialSource.Type {
case "vault", "static":
// Attempt unmarshaling into creds
if err := mapstructure.Decode(cred.Secret.Decoded, &c); err != nil {
return nil, err
}
}
if c.Username != "" && c.Password != "" {
out = append(out, c)
}
}
return out, nil
}
@@ -0,0 +1,231 @@
package connect
import (
"testing"
"github.com/hashicorp/boundary/api/targets"
"github.com/hashicorp/boundary/internal/credential"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_parseUsernamePasswordCredentials(t *testing.T) {
tests := []struct {
name string
creds []*targets.SessionCredential
wantCreds []usernamePasswordCredentials
wantErr bool
}{
{
name: "no-creds",
wantCreds: nil,
wantErr: false,
},
{
name: "no-credential-source",
creds: []*targets.SessionCredential{
{
Credential: map[string]interface{}{
"username": "user",
"password": "pass",
},
},
},
wantCreds: nil,
wantErr: true,
},
{
name: "valid-typed",
creds: []*targets.SessionCredential{
{
CredentialSource: &targets.CredentialSource{
CredentialType: string(credential.UsernamePasswordType),
},
Credential: map[string]interface{}{
"username": "user",
"password": "pass",
},
},
},
wantCreds: []usernamePasswordCredentials{
{
Username: "user",
Password: "pass",
},
},
wantErr: false,
},
{
name: "valid-typed-instead-of-decoded",
creds: []*targets.SessionCredential{
{
CredentialSource: &targets.CredentialSource{
CredentialType: string(credential.UsernamePasswordType),
},
Credential: map[string]interface{}{
"username": "user",
"password": "pass",
},
Secret: &targets.SessionSecret{
Decoded: map[string]interface{}{
"username": "secret-user",
"password": "secret-pass",
},
},
},
},
wantCreds: []usernamePasswordCredentials{
{
Username: "user",
Password: "pass",
},
},
wantErr: false,
},
{
name: "valid-vault-not-typed",
creds: []*targets.SessionCredential{
{
CredentialSource: &targets.CredentialSource{
Type: "vault",
},
Secret: &targets.SessionSecret{
Decoded: map[string]interface{}{
"username": "user",
"password": "pass",
},
},
},
},
wantCreds: []usernamePasswordCredentials{
{
Username: "user",
Password: "pass",
},
},
wantErr: false,
},
{
name: "valid-static-not-typed",
creds: []*targets.SessionCredential{
{
CredentialSource: &targets.CredentialSource{
Type: "static",
},
Secret: &targets.SessionSecret{
Decoded: map[string]interface{}{
"username": "user",
"password": "pass",
},
},
},
},
wantCreds: []usernamePasswordCredentials{
{
Username: "user",
Password: "pass",
},
},
wantErr: false,
},
{
name: "valid-multiple",
creds: []*targets.SessionCredential{
{
CredentialSource: &targets.CredentialSource{
CredentialType: string(credential.UsernamePasswordType),
},
Credential: map[string]interface{}{
"username": "user",
"password": "pass",
},
},
{
CredentialSource: &targets.CredentialSource{
CredentialType: string(credential.UsernamePasswordType),
},
Credential: map[string]interface{}{
"username": "user1",
"password": "pass1",
},
},
},
wantCreds: []usernamePasswordCredentials{
{
Username: "user",
Password: "pass",
},
{
Username: "user1",
Password: "pass1",
},
},
wantErr: false,
},
{
name: "valid-multiple-mixed",
creds: []*targets.SessionCredential{
{
CredentialSource: &targets.CredentialSource{
CredentialType: string(credential.UsernamePasswordType),
},
Credential: map[string]interface{}{
"username": "user",
"password": "pass",
},
},
{
CredentialSource: &targets.CredentialSource{
Type: "vault",
},
Secret: &targets.SessionSecret{
Decoded: map[string]interface{}{
"username": "user1",
"password": "pass1",
},
},
},
{
CredentialSource: &targets.CredentialSource{
Type: "static",
},
Secret: &targets.SessionSecret{
Decoded: map[string]interface{}{
"username": "user2",
"password": "pass2",
},
},
},
},
wantCreds: []usernamePasswordCredentials{
{
Username: "user",
Password: "pass",
},
{
Username: "user1",
Password: "pass1",
},
{
Username: "user2",
Password: "pass2",
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
creds, err := parseCredentials(tt.creds)
if tt.wantErr {
require.Error(err)
assert.Nil(creds)
return
}
require.NoError(err)
assert.ElementsMatch(tt.wantCreds, creds)
})
}
}
+1 -1
View File
@@ -78,7 +78,7 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string {
prefixStr := strings.Repeat(" ", indent)
origSecret := []string{fmt.Sprintf("%s %s", prefixStr, sc.Secret.Raw)}
switch sc.CredentialSource.Type {
case "vault":
case "vault", "static":
if sc.Credential != nil {
maxLength := 0
for k := range sc.Credential {
+14 -30
View File
@@ -7,7 +7,6 @@ import (
"strings"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/mitchellh/mapstructure"
"github.com/posener/complete"
)
@@ -52,33 +51,18 @@ func (p *postgresFlags) defaultExec() string {
return strings.ToLower(p.flagPostgresStyle)
}
type postgresCredentials struct {
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
}
func (p *postgresFlags) buildArgs(c *Command, port, ip, addr string) (args, envs []string, retErr error) {
var creds postgresCredentials
if c.sessionAuthz != nil && len(c.sessionAuthz.Credentials) > 0 {
for _, cred := range c.sessionAuthz.Credentials {
if cred.Secret == nil || cred.Secret.Decoded == nil {
continue
}
// TODO: Could allow switching on library ID or name
switch cred.CredentialLibrary.Type {
case "vault":
// Attempt unmarshaling into creds
if err := mapstructure.Decode(cred.Secret.Decoded, &creds); err != nil {
return nil, nil, fmt.Errorf("Error interpreting Vault secret: %w", err)
}
}
if creds.Username != "" && creds.Password != "" {
// In the future we can look for other types if we support other
// authentication mechanisms
break
}
var cred usernamePasswordCredentials
if c.sessionAuthz != nil {
creds, err := parseCredentials(c.sessionAuthz.Credentials)
if err != nil {
return nil, nil, fmt.Errorf("Error interpreting secret: %w", err)
}
if len(creds) > 0 {
// Just use first credentials returned
cred = creds[0]
}
}
switch p.flagPostgresStyle {
@@ -90,13 +74,13 @@ func (p *postgresFlags) buildArgs(c *Command, port, ip, addr string) (args, envs
}
switch {
case creds.Username != "":
args = append(args, "-U", creds.Username)
case cred.Username != "":
args = append(args, "-U", cred.Username)
case c.flagUsername != "":
args = append(args, "-U", c.flagUsername)
}
if creds.Password != "" {
if cred.Password != "" {
passfile, err := ioutil.TempFile("", "*")
if err != nil {
return nil, nil, fmt.Errorf("Error saving postgres password to tmp file: %w", err)
@@ -107,7 +91,7 @@ func (p *postgresFlags) buildArgs(c *Command, port, ip, addr string) (args, envs
}
return nil
})
_, err = passfile.WriteString(fmt.Sprintf("*:*:*:*:%s", creds.Password))
_, err = passfile.WriteString(fmt.Sprintf("*:*:*:*:%s", cred.Password))
if err != nil {
return nil, nil, fmt.Errorf("Error writing password file to %s: %w", passfile.Name(), err)
}
+43 -8
View File
@@ -1,6 +1,7 @@
package connect
import (
"errors"
"fmt"
"strings"
@@ -19,7 +20,7 @@ func sshOptions(c *Command, set *base.FlagSets) {
Name: "style",
Target: &c.flagSshStyle,
EnvVar: "BOUNDARY_CONNECT_SSH_STYLE",
Completion: complete.PredictSet("ssh", "putty"),
Completion: complete.PredictSet("ssh", "putty", "sshpass"),
Default: "ssh",
Usage: `Specifies how the CLI will attempt to invoke an SSH client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "ssh" and "putty".`,
})
@@ -41,18 +42,52 @@ func (s *sshFlags) defaultExec() string {
return strings.ToLower(s.flagSshStyle)
}
func (s *sshFlags) buildArgs(c *Command, port, ip, addr string) []string {
// Might want -t for ssh or -tt but seems fine without it for now...
var args []string
switch s.flagSshStyle {
func (s *sshFlags) buildArgs(c *Command, port, ip, addr string) (args, envs []string, retErr error) {
var cred usernamePasswordCredentials
if c.sessionAuthz != nil {
creds, err := parseCredentials(c.sessionAuthz.Credentials)
if err != nil {
return nil, nil, fmt.Errorf("Error interpreting secret: %w", err)
}
if len(creds) > 0 {
// Just use first credentials returned
cred = creds[0]
}
}
switch strings.ToLower(s.flagSshStyle) {
case "ssh":
// Might want -t for ssh or -tt but seems fine without it for now...
args = append(args, "-p", port, ip)
args = append(args, "-o", fmt.Sprintf("HostKeyAlias=%s", c.sessionAuthzData.HostId))
// SSH detects a host key change when localhost proxy port changes, disable localhost
// host key verification to avoid 'WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED...'
args = append(args, "-o", "NoHostAuthenticationForLocalhost=yes")
case "sshpass":
if cred.Password == "" {
return nil, nil, errors.New("Password is required when using sshpass")
}
// sshpass requires that the password is passed as env-var "SSHPASS"
// when the using env-vars.
envs = append(envs, fmt.Sprintf("SSHPASS=%s", cred.Password))
args = append(args, "-e", "ssh")
args = append(args, "-p", port, ip)
// sshpass cannot handle host key checking, disable localhost key verification
// to avoid error: 'SSHPASS detected host authentication prompt. Exiting.'
args = append(args, "-o", "NoHostAuthenticationForLocalhost=yes")
case "putty":
args = append(args, "-P", port, ip)
}
if c.flagUsername != "" {
switch {
case cred.Username != "":
args = append(args, "-l", cred.Username)
case c.flagUsername != "":
args = append(args, "-l", c.flagUsername)
}
return args
return args, envs, nil
}
@@ -0,0 +1,276 @@
// Code generated by "make cli"; DO NOT EDIT.
package credentialscmd
import (
"errors"
"fmt"
"sync"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/common"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
func initFlags() {
flagsOnce.Do(func() {
extraFlags := extraActionsFlagsMapFunc()
for k, v := range extraFlags {
flagsMap[k] = append(flagsMap[k], v...)
}
})
}
var (
_ cli.Command = (*Command)(nil)
_ cli.CommandAutocomplete = (*Command)(nil)
)
type Command struct {
*base.Command
Func string
plural string
}
func (c *Command) AutocompleteArgs() complete.Predictor {
initFlags()
return complete.PredictAnything
}
func (c *Command) AutocompleteFlags() complete.Flags {
initFlags()
return c.Flags().Completions()
}
func (c *Command) Synopsis() string {
if extra := extraSynopsisFunc(c); extra != "" {
return extra
}
synopsisStr := "credential"
return common.SynopsisFunc(c.Func, synopsisStr)
}
func (c *Command) Help() string {
initFlags()
var helpStr string
helpMap := common.HelpMap("credential")
switch c.Func {
case "read":
helpStr = helpMap[c.Func]() + c.Flags().Help()
case "delete":
helpStr = helpMap[c.Func]() + c.Flags().Help()
case "list":
helpStr = helpMap[c.Func]() + c.Flags().Help()
default:
helpStr = c.extraHelpFunc(helpMap)
}
// Keep linter from complaining if we don't actually generate code using it
_ = helpMap
return helpStr
}
var flagsMap = map[string][]string{
"read": {"id"},
"delete": {"id"},
"list": {"credential-store-id", "filter"},
}
func (c *Command) Flags() *base.FlagSets {
if len(flagsMap[c.Func]) == 0 {
return c.FlagSet(base.FlagSetNone)
}
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "credential", flagsMap, c.Func)
extraFlagsFunc(c, set, f)
return set
}
func (c *Command) Run(args []string) int {
initFlags()
switch c.Func {
case "":
return cli.RunResultHelp
case "create":
return cli.RunResultHelp
case "update":
return cli.RunResultHelp
}
c.plural = "credential"
switch c.Func {
case "list":
c.plural = "credentials"
}
f := c.Flags()
if err := f.Parse(args); err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" {
c.PrintCliError(errors.New("ID is required but not passed in via -id"))
return base.CommandUserError
}
var opts []credentials.Option
if strutil.StrListContains(flagsMap[c.Func], "credential-store-id") {
switch c.Func {
case "list":
if c.FlagCredentialStoreId == "" {
c.PrintCliError(errors.New("CredentialStore ID must be passed in via -credential-store-id or BOUNDARY_CREDENTIAL_STORE_ID"))
return base.CommandUserError
}
}
}
client, err := c.Client()
if c.WrapperCleanupFunc != nil {
defer func() {
if err := c.WrapperCleanupFunc(); err != nil {
c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err))
}
}()
}
if err != nil {
c.PrintCliError(fmt.Errorf("Error creating API client: %w", err))
return base.CommandCliError
}
credentialsClient := credentials.NewClient(client)
if c.FlagFilter != "" {
opts = append(opts, credentials.WithFilter(c.FlagFilter))
}
var version uint32
if ok := extraFlagsHandlingFunc(c, f, &opts); !ok {
return base.CommandUserError
}
var result api.GenericResult
var listResult api.GenericListResult
switch c.Func {
case "read":
result, err = credentialsClient.Read(c.Context, c.FlagId, opts...)
case "delete":
result, err = credentialsClient.Delete(c.Context, c.FlagId, opts...)
case "list":
listResult, err = credentialsClient.List(c.Context, c.FlagCredentialStoreId, opts...)
}
result, err = executeExtraActions(c, result, err, credentialsClient, version, opts)
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil {
var opts []base.Option
c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...)
return base.CommandApiError
}
c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error()))
return base.CommandCliError
}
output, err := printCustomActionOutput(c)
if err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if output {
return base.CommandSuccess
}
switch c.Func {
case "delete":
switch base.Format(c.UI) {
case "json":
if ok := c.PrintJsonItem(result); !ok {
return base.CommandCliError
}
case "table":
c.UI.Output("The delete operation completed successfully.")
}
return base.CommandSuccess
case "list":
switch base.Format(c.UI) {
case "json":
if ok := c.PrintJsonItems(listResult); !ok {
return base.CommandCliError
}
case "table":
listedItems := listResult.GetItems().([]*credentials.Credential)
c.UI.Output(c.printListTable(listedItems))
}
return base.CommandSuccess
}
switch base.Format(c.UI) {
case "table":
c.UI.Output(printItemTable(result))
case "json":
if ok := c.PrintJsonItem(result); !ok {
return base.CommandCliError
}
}
return base.CommandSuccess
}
var (
flagsOnce = new(sync.Once)
extraActionsFlagsMapFunc = func() map[string][]string { return nil }
extraSynopsisFunc = func(*Command) string { return "" }
extraFlagsFunc = func(*Command, *base.FlagSets, *base.FlagSet) {}
extraFlagsHandlingFunc = func(*Command, *base.FlagSets, *[]credentials.Option) bool { return true }
executeExtraActions = func(_ *Command, inResult api.GenericResult, inErr error, _ *credentials.Client, _ uint32, _ []credentials.Option) (api.GenericResult, error) {
return inResult, inErr
}
printCustomActionOutput = func(*Command) (bool, error) { return false, nil }
)
@@ -0,0 +1,177 @@
package credentialscmd
import (
"fmt"
"time"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/boundary/internal/cmd/base"
)
func (c *Command) extraHelpFunc(helpMap map[string]func() string) string {
var helpStr string
switch c.Func {
case "":
return base.WrapForHelpText([]string{
"Usage: boundary credentials [sub command] [options] [args]",
"",
" This command allows operations on Boundary credential resources. Example:",
"",
" Read a credential:",
"",
` $ boundary credentials read -id cred_1234567890`,
"",
" Please see the credentials subcommand help for detailed usage information.",
})
case "create":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary credentials create [type] [sub command] [options] [args]",
"",
" This command allows create operations on Boundary credential resources. Example:",
"",
" Create a credential:",
"",
` $ boundary credentials create username-password -credential-store-id cs_1234567890 -username user -password pass`,
"",
" Please see the typed subcommand help for detailed usage information.",
})
case "update":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary credentials update [type] [sub command] [options] [args]",
"",
" This command allows update operations on Boundary credential resources. Example:",
"",
" Update a user password credential:",
"",
` $ boundary credentials update username-password -id cred_1234567890 -name devops -description "For DevOps usage"`,
"",
" Please see the typed subcommand help for detailed usage information.",
})
default:
helpStr = helpMap["base"]()
}
return helpStr + c.Flags().Help()
}
func (c *Command) printListTable(items []*credentials.Credential) string {
if len(items) == 0 {
return "No credentials found"
}
var output []string
output = []string{
"",
"Credential information:",
}
for i, m := range items {
if i > 0 {
output = append(output, "")
}
if m.Id != "" {
output = append(output,
fmt.Sprintf(" ID: %s", m.Id),
)
} else {
output = append(output,
fmt.Sprintf(" ID: %s", "(not available)"),
)
}
if m.Version > 0 {
output = append(output,
fmt.Sprintf(" Version: %d", m.Version),
)
}
if m.Type != "" {
output = append(output,
fmt.Sprintf(" Type: %s", m.Type),
)
}
if m.Name != "" {
output = append(output,
fmt.Sprintf(" Name: %s", m.Name),
)
}
if m.Description != "" {
output = append(output,
fmt.Sprintf(" Description: %s", m.Description),
)
}
if len(m.AuthorizedActions) > 0 {
output = append(output,
" Authorized Actions:",
base.WrapSlice(6, m.AuthorizedActions),
)
}
}
return base.WrapForHelpText(output)
}
func printItemTable(result api.GenericResult) string {
item := result.GetItem().(*credentials.Credential)
nonAttributeMap := map[string]interface{}{}
if item.Id != "" {
nonAttributeMap["ID"] = item.Id
}
if item.Version != 0 {
nonAttributeMap["Version"] = item.Version
}
if !item.CreatedTime.IsZero() {
nonAttributeMap["Created Time"] = item.CreatedTime.Local().Format(time.RFC1123)
}
if !item.UpdatedTime.IsZero() {
nonAttributeMap["Updated Time"] = item.UpdatedTime.Local().Format(time.RFC1123)
}
if item.CredentialStoreId != "" {
nonAttributeMap["Credential Store ID"] = item.CredentialStoreId
}
if item.Name != "" {
nonAttributeMap["Name"] = item.Name
}
if item.Description != "" {
nonAttributeMap["Description"] = item.Description
}
if item.Type != "" {
nonAttributeMap["Type"] = item.Type
}
maxLength := base.MaxAttributesLength(nonAttributeMap, item.Attributes, keySubstMap)
ret := []string{
"",
"Credential information:",
base.WrapMap(2, maxLength+2, nonAttributeMap),
}
if item.Scope != nil {
ret = append(ret,
"",
" Scope:",
base.ScopeInfoForOutput(item.Scope, maxLength),
)
}
if len(item.AuthorizedActions) > 0 {
ret = append(ret,
"",
" Authorized Actions:",
base.WrapSlice(4, item.AuthorizedActions),
)
}
if len(item.Attributes) > 0 {
ret = append(ret,
"",
" Attributes:",
base.WrapMap(4, maxLength, item.Attributes),
)
}
return base.WrapForHelpText(ret)
}
var keySubstMap = map[string]string{
"username": "Username",
"password_hmac": "Password HMAC",
}
@@ -0,0 +1,258 @@
// Code generated by "make cli"; DO NOT EDIT.
package credentialscmd
import (
"errors"
"fmt"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/common"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
func initUsernamePasswordFlags() {
flagsOnce.Do(func() {
extraFlags := extraUsernamePasswordActionsFlagsMapFunc()
for k, v := range extraFlags {
flagsUsernamePasswordMap[k] = append(flagsUsernamePasswordMap[k], v...)
}
})
}
var (
_ cli.Command = (*UsernamePasswordCommand)(nil)
_ cli.CommandAutocomplete = (*UsernamePasswordCommand)(nil)
)
type UsernamePasswordCommand struct {
*base.Command
Func string
plural string
extraUsernamePasswordCmdVars
}
func (c *UsernamePasswordCommand) AutocompleteArgs() complete.Predictor {
initUsernamePasswordFlags()
return complete.PredictAnything
}
func (c *UsernamePasswordCommand) AutocompleteFlags() complete.Flags {
initUsernamePasswordFlags()
return c.Flags().Completions()
}
func (c *UsernamePasswordCommand) Synopsis() string {
if extra := extraUsernamePasswordSynopsisFunc(c); extra != "" {
return extra
}
synopsisStr := "credential"
synopsisStr = fmt.Sprintf("%s %s", "username_password-type", synopsisStr)
return common.SynopsisFunc(c.Func, synopsisStr)
}
func (c *UsernamePasswordCommand) Help() string {
initUsernamePasswordFlags()
var helpStr string
helpMap := common.HelpMap("credential")
switch c.Func {
default:
helpStr = c.extraUsernamePasswordHelpFunc(helpMap)
}
// Keep linter from complaining if we don't actually generate code using it
_ = helpMap
return helpStr
}
var flagsUsernamePasswordMap = map[string][]string{
"create": {"credential-store-id", "name", "description"},
"update": {"id", "name", "description", "version"},
}
func (c *UsernamePasswordCommand) Flags() *base.FlagSets {
if len(flagsUsernamePasswordMap[c.Func]) == 0 {
return c.FlagSet(base.FlagSetNone)
}
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "username_password-type credential", flagsUsernamePasswordMap, c.Func)
extraUsernamePasswordFlagsFunc(c, set, f)
return set
}
func (c *UsernamePasswordCommand) Run(args []string) int {
initUsernamePasswordFlags()
switch c.Func {
case "":
return cli.RunResultHelp
}
c.plural = "username_password-type credential"
switch c.Func {
case "list":
c.plural = "username_password-type credentials"
}
f := c.Flags()
if err := f.Parse(args); err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if strutil.StrListContains(flagsUsernamePasswordMap[c.Func], "id") && c.FlagId == "" {
c.PrintCliError(errors.New("ID is required but not passed in via -id"))
return base.CommandUserError
}
var opts []credentials.Option
if strutil.StrListContains(flagsUsernamePasswordMap[c.Func], "credential-store-id") {
switch c.Func {
case "create":
if c.FlagCredentialStoreId == "" {
c.PrintCliError(errors.New("CredentialStore ID must be passed in via -credential-store-id or BOUNDARY_CREDENTIAL_STORE_ID"))
return base.CommandUserError
}
}
}
client, err := c.Client()
if c.WrapperCleanupFunc != nil {
defer func() {
if err := c.WrapperCleanupFunc(); err != nil {
c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err))
}
}()
}
if err != nil {
c.PrintCliError(fmt.Errorf("Error creating API client: %w", err))
return base.CommandCliError
}
credentialsClient := credentials.NewClient(client)
switch c.FlagName {
case "":
case "null":
opts = append(opts, credentials.DefaultName())
default:
opts = append(opts, credentials.WithName(c.FlagName))
}
switch c.FlagDescription {
case "":
case "null":
opts = append(opts, credentials.DefaultDescription())
default:
opts = append(opts, credentials.WithDescription(c.FlagDescription))
}
if c.FlagFilter != "" {
opts = append(opts, credentials.WithFilter(c.FlagFilter))
}
var version uint32
switch c.Func {
case "update":
switch c.FlagVersion {
case 0:
opts = append(opts, credentials.WithAutomaticVersioning(true))
default:
version = uint32(c.FlagVersion)
}
}
if ok := extraUsernamePasswordFlagsHandlingFunc(c, f, &opts); !ok {
return base.CommandUserError
}
var result api.GenericResult
switch c.Func {
case "create":
result, err = credentialsClient.Create(c.Context, "username_password", c.FlagCredentialStoreId, opts...)
case "update":
result, err = credentialsClient.Update(c.Context, c.FlagId, version, opts...)
}
result, err = executeExtraUsernamePasswordActions(c, result, err, credentialsClient, version, opts)
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil {
var opts []base.Option
opts = append(opts, base.WithAttributeFieldPrefix("username_password"))
c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...)
return base.CommandApiError
}
c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error()))
return base.CommandCliError
}
output, err := printCustomUsernamePasswordActionOutput(c)
if err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if output {
return base.CommandSuccess
}
switch c.Func {
}
switch base.Format(c.UI) {
case "table":
c.UI.Output(printItemTable(result))
case "json":
if ok := c.PrintJsonItem(result); !ok {
return base.CommandCliError
}
}
return base.CommandSuccess
}
var (
extraUsernamePasswordActionsFlagsMapFunc = func() map[string][]string { return nil }
extraUsernamePasswordSynopsisFunc = func(*UsernamePasswordCommand) string { return "" }
extraUsernamePasswordFlagsFunc = func(*UsernamePasswordCommand, *base.FlagSets, *base.FlagSet) {}
extraUsernamePasswordFlagsHandlingFunc = func(*UsernamePasswordCommand, *base.FlagSets, *[]credentials.Option) bool { return true }
executeExtraUsernamePasswordActions = func(_ *UsernamePasswordCommand, inResult api.GenericResult, inErr error, _ *credentials.Client, _ uint32, _ []credentials.Option) (api.GenericResult, error) {
return inResult, inErr
}
printCustomUsernamePasswordActionOutput = func(*UsernamePasswordCommand) (bool, error) { return false, nil }
)
@@ -0,0 +1,97 @@
package credentialscmd
import (
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/boundary/internal/cmd/base"
)
func init() {
extraUsernamePasswordFlagsFunc = extraUsernamePasswordFlagsFuncImpl
extraUsernamePasswordActionsFlagsMapFunc = extraUsernamePasswordActionsFlagsMapFuncImpl
extraUsernamePasswordFlagsHandlingFunc = extraUsernamePasswordFlagHandlingFuncImpl
}
const (
usernameFlagName = "username"
passwordFlagName = "password"
)
type extraUsernamePasswordCmdVars struct {
flagUsername string
flagPassword string
}
func extraUsernamePasswordActionsFlagsMapFuncImpl() map[string][]string {
flags := map[string][]string{
"create": {
usernameFlagName,
passwordFlagName,
},
}
flags["update"] = flags["create"]
return flags
}
func extraUsernamePasswordFlagsFuncImpl(c *UsernamePasswordCommand, set *base.FlagSets, _ *base.FlagSet) {
f := set.NewFlagSet("User Password Credential Options")
for _, name := range flagsUsernamePasswordMap[c.Func] {
switch name {
case usernameFlagName:
f.StringVar(&base.StringVar{
Name: usernameFlagName,
Target: &c.flagUsername,
Usage: "The username associated with the credential.",
})
case passwordFlagName:
f.StringVar(&base.StringVar{
Name: passwordFlagName,
Target: &c.flagPassword,
Usage: "The password associated with the credential.",
})
}
}
}
func extraUsernamePasswordFlagHandlingFuncImpl(c *UsernamePasswordCommand, _ *base.FlagSets, opts *[]credentials.Option) bool {
switch c.flagUsername {
case "":
default:
*opts = append(*opts, credentials.WithUsernamePasswordCredentialUsername(c.flagUsername))
}
switch c.flagPassword {
case "":
default:
*opts = append(*opts, credentials.WithUsernamePasswordCredentialPassword(c.flagPassword))
}
return true
}
func (c *UsernamePasswordCommand) extraUsernamePasswordHelpFunc(_ map[string]func() string) string {
var helpStr string
switch c.Func {
case "create":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary credentials create username-password -credential-store-id [options] [args]",
"",
" Create a user password credential. Example:",
"",
` $ boundary credentials create username-password -credential-store-id csvlt_1234567890 -username user -password pass`,
"",
"",
})
case "update":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary credentials update user password [options] [args]",
"",
" Update a user password credential given its ID. Example:",
"",
` $ boundary credentials update username-password -id clvlt_1234567890 -name devops -description "For DevOps usage"`,
"",
"",
})
}
return helpStr + c.Flags().Help()
}
@@ -33,7 +33,11 @@ func (c *Command) extraHelpFunc(helpMap map[string]func() string) string {
"",
" Create a vault-type credential store:",
"",
` $ boundary credential-stores create vault -vault-address "http://localhost:8200" -vault-token "s.s0m3t0k3n"`,
` $ boundary credential-stores create vault -scope-id p_1234567890 -vault-address "http://localhost:8200" -vault-token "s.s0m3t0k3n"`,
"",
" Create a static-type credential store:",
"",
` $ boundary credential-stores create static -scope-id p_1234567890`,
"",
" Please see the typed subcommand help for detailed usage information.",
})
@@ -47,6 +51,10 @@ func (c *Command) extraHelpFunc(helpMap map[string]func() string) string {
"",
` $ boundary credential-stores update vault -id csvlt_1234567890 -name devops -description "For DevOps usage"`,
"",
" Update a static-type credential store:",
"",
` $ boundary credential-stores update static -id cs_1234567890 -name devops -description "For DevOps usage"`,
"",
" Please see the typed subcommand help for detailed usage information.",
})
default:
@@ -0,0 +1,261 @@
// Code generated by "make cli"; DO NOT EDIT.
package credentialstorescmd
import (
"errors"
"fmt"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentialstores"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/common"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
func initStaticFlags() {
flagsOnce.Do(func() {
extraFlags := extraStaticActionsFlagsMapFunc()
for k, v := range extraFlags {
flagsStaticMap[k] = append(flagsStaticMap[k], v...)
}
})
}
var (
_ cli.Command = (*StaticCommand)(nil)
_ cli.CommandAutocomplete = (*StaticCommand)(nil)
)
type StaticCommand struct {
*base.Command
Func string
plural string
}
func (c *StaticCommand) AutocompleteArgs() complete.Predictor {
initStaticFlags()
return complete.PredictAnything
}
func (c *StaticCommand) AutocompleteFlags() complete.Flags {
initStaticFlags()
return c.Flags().Completions()
}
func (c *StaticCommand) Synopsis() string {
if extra := extraStaticSynopsisFunc(c); extra != "" {
return extra
}
synopsisStr := "credential store"
synopsisStr = fmt.Sprintf("%s %s", "static-type", synopsisStr)
return common.SynopsisFunc(c.Func, synopsisStr)
}
func (c *StaticCommand) Help() string {
initStaticFlags()
var helpStr string
helpMap := common.HelpMap("credential store")
switch c.Func {
default:
helpStr = c.extraStaticHelpFunc(helpMap)
}
// Keep linter from complaining if we don't actually generate code using it
_ = helpMap
return helpStr
}
var flagsStaticMap = map[string][]string{
"create": {"scope-id", "name", "description"},
"update": {"id", "name", "description", "version"},
}
func (c *StaticCommand) Flags() *base.FlagSets {
if len(flagsStaticMap[c.Func]) == 0 {
return c.FlagSet(base.FlagSetNone)
}
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "static-type credential store", flagsStaticMap, c.Func)
extraStaticFlagsFunc(c, set, f)
return set
}
func (c *StaticCommand) Run(args []string) int {
initStaticFlags()
switch c.Func {
case "":
return cli.RunResultHelp
}
c.plural = "static-type credential store"
switch c.Func {
case "list":
c.plural = "static-type credential stores"
}
f := c.Flags()
if err := f.Parse(args); err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if strutil.StrListContains(flagsStaticMap[c.Func], "id") && c.FlagId == "" {
c.PrintCliError(errors.New("ID is required but not passed in via -id"))
return base.CommandUserError
}
var opts []credentialstores.Option
if strutil.StrListContains(flagsStaticMap[c.Func], "scope-id") {
switch c.Func {
case "create":
if c.FlagScopeId == "" {
c.PrintCliError(errors.New("Scope ID must be passed in via -scope-id or BOUNDARY_SCOPE_ID"))
return base.CommandUserError
}
}
}
client, err := c.Client()
if c.WrapperCleanupFunc != nil {
defer func() {
if err := c.WrapperCleanupFunc(); err != nil {
c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err))
}
}()
}
if err != nil {
c.PrintCliError(fmt.Errorf("Error creating API client: %w", err))
return base.CommandCliError
}
credentialstoresClient := credentialstores.NewClient(client)
switch c.FlagName {
case "":
case "null":
opts = append(opts, credentialstores.DefaultName())
default:
opts = append(opts, credentialstores.WithName(c.FlagName))
}
switch c.FlagDescription {
case "":
case "null":
opts = append(opts, credentialstores.DefaultDescription())
default:
opts = append(opts, credentialstores.WithDescription(c.FlagDescription))
}
switch c.FlagRecursive {
case true:
opts = append(opts, credentialstores.WithRecursive(true))
}
if c.FlagFilter != "" {
opts = append(opts, credentialstores.WithFilter(c.FlagFilter))
}
var version uint32
switch c.Func {
case "update":
switch c.FlagVersion {
case 0:
opts = append(opts, credentialstores.WithAutomaticVersioning(true))
default:
version = uint32(c.FlagVersion)
}
}
if ok := extraStaticFlagsHandlingFunc(c, f, &opts); !ok {
return base.CommandUserError
}
var result api.GenericResult
switch c.Func {
case "create":
result, err = credentialstoresClient.Create(c.Context, "static", c.FlagScopeId, opts...)
case "update":
result, err = credentialstoresClient.Update(c.Context, c.FlagId, version, opts...)
}
result, err = executeExtraStaticActions(c, result, err, credentialstoresClient, version, opts)
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil {
var opts []base.Option
opts = append(opts, base.WithAttributeFieldPrefix("static"))
c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...)
return base.CommandApiError
}
c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error()))
return base.CommandCliError
}
output, err := printCustomStaticActionOutput(c)
if err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if output {
return base.CommandSuccess
}
switch c.Func {
}
switch base.Format(c.UI) {
case "table":
c.UI.Output(printItemTable(result))
case "json":
if ok := c.PrintJsonItem(result); !ok {
return base.CommandCliError
}
}
return base.CommandSuccess
}
var (
extraStaticActionsFlagsMapFunc = func() map[string][]string { return nil }
extraStaticSynopsisFunc = func(*StaticCommand) string { return "" }
extraStaticFlagsFunc = func(*StaticCommand, *base.FlagSets, *base.FlagSet) {}
extraStaticFlagsHandlingFunc = func(*StaticCommand, *base.FlagSets, *[]credentialstores.Option) bool { return true }
executeExtraStaticActions = func(_ *StaticCommand, inResult api.GenericResult, inErr error, _ *credentialstores.Client, _ uint32, _ []credentialstores.Option) (api.GenericResult, error) {
return inResult, inErr
}
printCustomStaticActionOutput = func(*StaticCommand) (bool, error) { return false, nil }
)
@@ -0,0 +1,33 @@
package credentialstorescmd
import (
"github.com/hashicorp/boundary/internal/cmd/base"
)
func (c *StaticCommand) extraStaticHelpFunc(helpMap map[string]func() string) string {
var helpStr string
switch c.Func {
case "create":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary credential-stores create static [options] [args]",
"",
" Create a static-type credential store. Example:",
"",
` $ boundary credential-stores create static -scope-id p_1234567890`,
"",
"",
})
case "update":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary credential-stores update static [options] [args]",
"",
" Update a static-type credential store given its ID. Example:",
"",
` $ boundary credential-stores update static -name devops -description "For DevOps usage"`,
"",
"",
})
}
return helpStr + c.Flags().Help()
}
+17 -126
View File
@@ -30,30 +30,26 @@ func init() {
}
type extraCmdVars struct {
flagHostSets []string
flagHostSources []string
flagApplicationCredentialLibraries []string
flagApplicationCredentialSources []string
flagEgressCredentialSources []string
flagHostId string
sar *targets.SessionAuthorizationResult
flagHostSets []string
flagHostSources []string
flagApplicationCredentialSources []string
flagEgressCredentialSources []string
flagHostId string
sar *targets.SessionAuthorizationResult
}
func extraActionsFlagsMapFuncImpl() map[string][]string {
return map[string][]string{
"authorize-session": {"id", "host-id"},
"add-host-sets": {"id", "host-set", "version"},
"remove-host-sets": {"id", "host-set", "version"},
"set-host-sets": {"id", "host-set", "version"},
"add-host-sources": {"id", "host-source", "version"},
"remove-host-sources": {"id", "host-source", "version"},
"set-host-sources": {"id", "host-source", "version"},
"add-credential-libraries": {"id", "application-credential-library", "version"},
"remove-credential-libraries": {"id", "application-credential-library", "version"},
"set-credential-libraries": {"id", "application-credential-library", "version"},
"add-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"},
"remove-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"},
"set-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"},
"authorize-session": {"id", "host-id"},
"add-host-sets": {"id", "host-set", "version"},
"remove-host-sets": {"id", "host-set", "version"},
"set-host-sets": {"id", "host-set", "version"},
"add-host-sources": {"id", "host-source", "version"},
"remove-host-sources": {"id", "host-source", "version"},
"set-host-sources": {"id", "host-source", "version"},
"add-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"},
"remove-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"},
"set-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"},
}
}
@@ -83,18 +79,6 @@ func extraSynopsisFuncImpl(c *Command) string {
}
return wordwrap.WrapString(fmt.Sprintf("%s a target", in), base.TermWidth)
case "add-credential-libraries", "set-credential-libraries", "remove-credential-libraries":
var in string
switch {
case strings.HasPrefix(c.Func, "add"):
in = "Add credential libraries to"
case strings.HasPrefix(c.Func, "set"):
in = "Set the full contents of the credential libraries on"
case strings.HasPrefix(c.Func, "remove"):
in = "Remove credential libraries from"
}
return wordwrap.WrapString(fmt.Sprintf("%s a target. DEPRECATED; use the -sources version instead.", in), base.TermWidth)
case "add-credential-sources", "set-credential-sources", "remove-credential-sources":
var in string
switch {
@@ -232,48 +216,6 @@ func (c *Command) extraHelpFunc(helpMap map[string]func() string) string {
"",
"",
})
case "add-credential-libraries":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary target add-credential-libraries [options] [args]",
"",
" DEPRECATED: Use add-credential-sources instead.",
"",
" This command allows adding credential-library resources to target resources. Example:",
"",
" Add credential-library resources to a tcp-type target:",
"",
` $ boundary targets add-credential-libraries -id ttcp_1234567890 -application-credential-library clvlt_1234567890 -application-credential-library clvlt_0987654321`,
"",
"",
})
case "remove-credential-libraries":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary target remove-credential-libraries [options] [args]",
"",
" DEPRECATED: Use remove-credential-sources instead.",
"",
" This command allows removing credential-library resources from target resources. Example:",
"",
" Remove credential-library resources from a tcp-type target:",
"",
` $ boundary targets remove-credential-libraries -id ttcp_1234567890 -application-credential-library clvlt_1234567890 -application-credential-library clvlt_0987654321`,
"",
"",
})
case "set-credential-libraries":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary target set-credential-libraries [options] [args]",
"",
" DEPRECATED: Use set-credential-sources instead.",
"",
" This command allows setting the complete set of credential-library resources on a target resource. Example:",
"",
" Set credential-library resources on a tcp-type target:",
"",
` $ boundary targets set-credential-libraries -id ttcp_1234567890 -application-credential-library clvlt_1234567890`,
"",
"",
})
case "add-credential-sources":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary target add-credential-sources [options] [args]",
@@ -351,12 +293,6 @@ func extraFlagsFuncImpl(c *Command, _ *base.FlagSets, f *base.FlagSet) {
Target: &c.flagHostId,
Usage: "The ID of a specific host to connect to out of the hosts from the target's host sets. If not specified, one is chosen at random.",
})
case "application-credential-library":
f.StringSliceVar(&base.StringSliceVar{
Name: "application-credential-library",
Target: &c.flagApplicationCredentialLibraries,
Usage: "The credential-library resource for application purpose to add, set, or remove. May be specified multiple times.",
})
case "application-credential-source":
f.StringSliceVar(&base.StringSliceVar{
Name: "application-credential-source",
@@ -467,13 +403,6 @@ func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]targets.Op
}
}
case "add-credential-libraries", "remove-credential-libraries":
if len(c.flagApplicationCredentialLibraries) == 0 {
c.UI.Error("No credential-libraries supplied via -application-credential-library")
return false
}
*opts = append(*opts, targets.WithApplicationCredentialLibraryIds(c.flagApplicationCredentialLibraries))
case "add-credential-sources", "remove-credential-sources":
// TODO: As we add other purposes, add them to this check
if len(c.flagApplicationCredentialSources)+len(c.flagEgressCredentialSources) == 0 {
@@ -488,21 +417,6 @@ func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]targets.Op
*opts = append(*opts, targets.WithEgressCredentialSourceIds(c.flagEgressCredentialSources))
}
case "set-credential-libraries":
switch len(c.flagApplicationCredentialLibraries) {
case 0:
c.UI.Error("No credential-libraries supplied via -application-credential-library")
return false
case 1:
if c.flagApplicationCredentialLibraries[0] == "null" {
*opts = append(*opts, targets.DefaultApplicationCredentialLibraryIds())
break
}
fallthrough
default:
*opts = append(*opts, targets.WithApplicationCredentialLibraryIds(c.flagApplicationCredentialLibraries))
}
case "set-credential-sources":
// TODO: As we add other purposes, add them to this check
if len(c.flagApplicationCredentialSources)+len(c.flagEgressCredentialSources) == 0 {
@@ -558,12 +472,6 @@ func executeExtraActionsImpl(c *Command, origResult api.GenericResult, origError
return targetClient.RemoveHostSources(c.Context, c.FlagId, version, c.flagHostSources, opts...)
case "set-host-sources":
return targetClient.SetHostSources(c.Context, c.FlagId, version, c.flagHostSources, opts...)
case "add-credential-libraries":
return targetClient.AddCredentialLibraries(c.Context, c.FlagId, version, opts...)
case "remove-credential-libraries":
return targetClient.RemoveCredentialLibraries(c.Context, c.FlagId, version, opts...)
case "set-credential-libraries":
return targetClient.SetCredentialLibraries(c.Context, c.FlagId, version, opts...)
case "add-credential-sources":
return targetClient.AddCredentialSources(c.Context, c.FlagId, version, opts...)
case "remove-credential-sources":
@@ -702,23 +610,6 @@ func printItemTable(result api.GenericResult) string {
}
var credentialSourceMaps map[credential.Purpose][]map[string]interface{}
if len(item.ApplicationCredentialLibraries) > 0 {
if credentialSourceMaps == nil {
credentialSourceMaps = make(map[credential.Purpose][]map[string]interface{})
}
var applicationCredentialSourceMaps []map[string]interface{}
for _, lib := range item.ApplicationCredentialLibraries {
m := map[string]interface{}{
"ID": lib.Id,
"Credential Store ID": lib.CredentialStoreId,
}
applicationCredentialSourceMaps = append(applicationCredentialSourceMaps, m)
}
credentialSourceMaps[credential.ApplicationPurpose] = applicationCredentialSourceMaps
if l := len("Credential Store ID"); l > maxLength {
maxLength = l
}
}
if len(item.ApplicationCredentialSources) > 0 {
if credentialSourceMaps == nil {
credentialSourceMaps = make(map[credential.Purpose][]map[string]interface{})
@@ -888,7 +779,7 @@ func printCustomActionOutputImpl(c *Command) (bool, error) {
var secretStr []string
switch cred.CredentialSource.Type {
case "vault":
case "vault", "static":
switch {
case cred.Credential != nil:
maxLength := 0
@@ -249,30 +249,6 @@ func (c *Command) Run(args []string) int {
version = uint32(c.FlagVersion)
}
case "add-credential-libraries":
switch c.FlagVersion {
case 0:
opts = append(opts, targets.WithAutomaticVersioning(true))
default:
version = uint32(c.FlagVersion)
}
case "remove-credential-libraries":
switch c.FlagVersion {
case 0:
opts = append(opts, targets.WithAutomaticVersioning(true))
default:
version = uint32(c.FlagVersion)
}
case "set-credential-libraries":
switch c.FlagVersion {
case 0:
opts = append(opts, targets.WithAutomaticVersioning(true))
default:
version = uint32(c.FlagVersion)
}
case "add-credential-sources":
switch c.FlagVersion {
case 0:
+42 -1
View File
@@ -197,6 +197,21 @@ var inputStructs = map[string][]*cmdInfo{
NeedsSubtypeInCreate: true,
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
},
{
ResourceType: resource.CredentialStore.String(),
Pkg: "credentialstores",
StdActions: []string{"create", "update"},
SubActionPrefix: "static",
SkipNormalHelp: true,
HasExtraHelpFunc: true,
HasId: true,
HasName: true,
HasDescription: true,
Container: "Scope",
VersionedActions: []string{"update"},
NeedsSubtypeInCreate: true,
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
},
},
"credentiallibraries": {
{
@@ -223,6 +238,32 @@ var inputStructs = map[string][]*cmdInfo{
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
},
},
"credentials": {
{
ResourceType: resource.Credential.String(),
Pkg: "credentials",
StdActions: []string{"read", "delete", "list"},
HasExtraHelpFunc: true,
Container: "CredentialStore",
HasId: true,
},
{
ResourceType: resource.Credential.String(),
Pkg: "credentials",
StdActions: []string{"create", "update"},
SubActionPrefix: "username_password",
HasExtraCommandVars: true,
SkipNormalHelp: true,
HasExtraHelpFunc: true,
HasId: true,
HasName: true,
HasDescription: true,
Container: "CredentialStore",
VersionedActions: []string{"update"},
NeedsSubtypeInCreate: true,
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
},
},
"groups": {
{
ResourceType: resource.Group.String(),
@@ -421,7 +462,7 @@ var inputStructs = map[string][]*cmdInfo{
HasName: true,
HasDescription: true,
Container: "Scope",
VersionedActions: []string{"add-host-sets", "remove-host-sets", "set-host-sets", "add-host-sources", "remove-host-sources", "set-host-sources", "add-credential-libraries", "remove-credential-libraries", "set-credential-libraries", "add-credential-sources", "remove-credential-sources", "set-credential-sources"},
VersionedActions: []string{"add-host-sets", "remove-host-sets", "set-host-sets", "add-host-sources", "remove-host-sources", "set-host-sources", "add-credential-sources", "remove-credential-sources", "set-credential-sources"},
},
{
ResourceType: resource.Target.String(),
+10 -4
View File
@@ -26,8 +26,8 @@ type Type string
// Credential type values.
const (
UnspecifiedType Type = "unspecified"
UserPasswordType Type = "user_password"
UnspecifiedType Type = "unspecified"
UsernamePasswordType Type = "username_password"
)
// A Library is a resource that provides credentials that are of the same
@@ -86,6 +86,12 @@ type Dynamic interface {
Purpose() Purpose
}
// Static is a static credential that is stored directly in a credential store.
type Static interface {
boundary.Resource
GetStoreId() string
}
// A Request represents a request for a credential from the SourceId for
// the given purpose. For dynamic credentials, the SourceId is the PublicId
// of a credential library.
@@ -118,8 +124,8 @@ type Password string
// PrivateKey represents a secret private key.
type PrivateKey []byte
// UserPassword is a credential containing a username and a password.
type UserPassword interface {
// UsernamePassword is a credential containing a username and a password.
type UsernamePassword interface {
Credential
Username() string
Password() Password
@@ -0,0 +1,65 @@
package static
import (
"github.com/hashicorp/boundary/internal/credential/static/store"
"github.com/hashicorp/boundary/internal/oplog"
"google.golang.org/protobuf/proto"
)
// A CredentialStore contains credentials. It is owned by a scope.
type CredentialStore struct {
*store.CredentialStore
tableName string `gorm:"-"`
}
// NewCredentialStore creates a new in memory static CredentialStore assigned to scopeId.
// Name and description are the only valid options. All other options are ignored.
func NewCredentialStore(scopeId string, opt ...Option) (*CredentialStore, error) {
opts := getOpts(opt...)
cs := &CredentialStore{
CredentialStore: &store.CredentialStore{
ScopeId: scopeId,
Name: opts.withName,
Description: opts.withDescription,
},
}
return cs, nil
}
func allocCredentialStore() *CredentialStore {
return &CredentialStore{
CredentialStore: &store.CredentialStore{},
}
}
func (cs *CredentialStore) clone() *CredentialStore {
cp := proto.Clone(cs.CredentialStore)
return &CredentialStore{
CredentialStore: cp.(*store.CredentialStore),
}
}
// TableName returns the table name.
func (cs *CredentialStore) TableName() string {
if cs.tableName != "" {
return cs.tableName
}
return "credential_static_store"
}
// SetTableName sets the table name.
func (cs *CredentialStore) SetTableName(n string) {
cs.tableName = n
}
func (cs *CredentialStore) oplog(op oplog.OpType) oplog.Metadata {
metadata := oplog.Metadata{
"resource-public-id": []string{cs.PublicId},
"resource-type": []string{"credential-static-store"},
"op-type": []string{op.String()},
}
if cs.ScopeId != "" {
metadata["scope-id"] = []string{cs.ScopeId}
}
return metadata
}
@@ -0,0 +1,111 @@
package static
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/boundary/internal/credential/static/store"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/iam"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/testing/protocmp"
)
func TestCredentialStore_New(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
rw := db.New(conn)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
scope := prj
type args struct {
scopeId string
opts []Option
}
tests := []struct {
name string
args args
want *CredentialStore
wantCreateErr bool
}{
{
name: "missing-scope-id",
want: allocCredentialStore(),
wantCreateErr: true,
},
{
name: "valid-no-options",
args: args{
scopeId: scope.PublicId,
},
want: &CredentialStore{
CredentialStore: &store.CredentialStore{
ScopeId: scope.PublicId,
},
},
},
{
name: "valid-with-name",
args: args{
scopeId: scope.PublicId,
opts: []Option{
WithName("test-name"),
},
},
want: &CredentialStore{
CredentialStore: &store.CredentialStore{
ScopeId: scope.PublicId,
Name: "test-name",
},
},
},
{
name: "valid-with-description",
args: args{
scopeId: scope.PublicId,
opts: []Option{
WithDescription("test-description"),
},
},
want: &CredentialStore{
CredentialStore: &store.CredentialStore{
ScopeId: scope.PublicId,
Description: "test-description",
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := NewCredentialStore(tt.args.scopeId, tt.args.opts...)
require.NoError(err)
require.NotNil(got)
assert.Emptyf(got.PublicId, "PublicId set")
assert.Equal(tt.want, got)
assert.Empty(cmp.Diff(tt.want, got.clone(), protocmp.Transform()))
id, err := newCredentialStoreId(context.Background())
assert.NoError(err)
tt.want.PublicId = id
got.PublicId = id
err = rw.Create(context.Background(), got)
if tt.wantCreateErr {
require.Error(err)
return
}
assert.NoError(err)
})
}
}
+2
View File
@@ -0,0 +1,2 @@
// Package static implements a credential store for static credentials.
package static
+9
View File
@@ -0,0 +1,9 @@
package static
// These constants are the field names used in the static related field masks.
const (
nameField = "Name"
descriptionField = "Description"
usernameField = "Username"
passwordField = "Password"
)
+47
View File
@@ -0,0 +1,47 @@
package static
// getOpts - iterate the inbound Options and return a struct
func getOpts(opt ...Option) options {
opts := getDefaultOptions()
for _, o := range opt {
o(&opts)
}
return opts
}
// Option - how Options are passed as arguments.
type Option func(*options)
// options = how options are represented
type options struct {
withName string
withDescription string
withLimit int
}
func getDefaultOptions() options {
return options{}
}
// WithDescription provides an optional description.
func WithDescription(desc string) Option {
return func(o *options) {
o.withDescription = desc
}
}
// WithName provides an optional name.
func WithName(name string) Option {
return func(o *options) {
o.withName = name
}
}
// WithLimit provides an option to provide a limit. Intentionally allowing
// negative integers. If WithLimit < 0, then unlimited results are
// returned. If WithLimit == 0, then default limits are used for results.
func WithLimit(l int) Option {
return func(o *options) {
o.withLimit = l
}
}
@@ -0,0 +1,29 @@
package static
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_GetOpts(t *testing.T) {
t.Parallel()
t.Run("WithName", func(t *testing.T) {
opts := getOpts(WithName("test"))
testOpts := getDefaultOptions()
testOpts.withName = "test"
assert.Equal(t, opts, testOpts)
})
t.Run("WithDescription", func(t *testing.T) {
opts := getOpts(WithDescription("test desc"))
testOpts := getDefaultOptions()
testOpts.withDescription = "test desc"
assert.Equal(t, opts, testOpts)
})
t.Run("WithLimit", func(t *testing.T) {
opts := getOpts(WithLimit(5))
testOpts := getDefaultOptions()
testOpts.withLimit = 5
assert.Equal(t, opts, testOpts)
})
}
+44
View File
@@ -0,0 +1,44 @@
package static
import (
"context"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/types/subtypes"
)
func init() {
if err := subtypes.Register(credential.Domain, Subtype, CredentialStorePrefix); err != nil {
panic(err)
}
if err := subtypes.Register(credential.Domain, UsernamePasswordSubtype, CredentialPrefix); err != nil {
panic(err)
}
}
// PublicId prefixes for the resources in the static package.
const (
CredentialStorePrefix = "cs"
CredentialPrefix = "cred"
Subtype = subtypes.Subtype("static")
UsernamePasswordSubtype = subtypes.Subtype("username_password")
)
func newCredentialStoreId(ctx context.Context) (string, error) {
id, err := db.NewPublicId(CredentialStorePrefix)
if err != nil {
return "", errors.Wrap(ctx, err, "static.newCredentialStoreId")
}
return id, nil
}
func newCredentialId(ctx context.Context) (string, error) {
id, err := db.NewPublicId(CredentialPrefix)
if err != nil {
return "", errors.Wrap(ctx, err, "static.newCredentialId")
}
return id, nil
}
+49
View File
@@ -0,0 +1,49 @@
package static
import (
"context"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
)
// A Repository stores and retrieves the persistent types in the static
// package. It is not safe to use a repository concurrently.
type Repository struct {
reader db.Reader
writer db.Writer
kms *kms.Kms
// defaultLimit provides a default for limiting the number of results
// returned from the repo
defaultLimit int
}
// NewRepository creates a new Repository. The returned repository should
// only be used for one transaction and it is not safe for concurrent go
// routines to access it. WithLimit option is used as a repo wide default
// limit applied to all ListX methods.
func NewRepository(ctx context.Context, r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) {
const op = "static.NewRepository"
switch {
case r == nil:
return nil, errors.New(ctx, errors.InvalidParameter, op, "db.Reader")
case w == nil:
return nil, errors.New(ctx, errors.InvalidParameter, op, "db.Writer")
case kms == nil:
return nil, errors.New(ctx, errors.InvalidParameter, op, "kms")
}
opts := getOpts(opt...)
if opts.withLimit == 0 {
// zero signals the boundary defaults should be used.
opts.withLimit = db.DefaultLimit
}
return &Repository{
reader: r,
writer: w,
kms: kms,
defaultLimit: opts.withLimit,
}, nil
}
@@ -0,0 +1,309 @@
package static
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/db"
dbcommon "github.com/hashicorp/boundary/internal/db/common"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
)
// CreateUsernamePasswordCredential inserts c into the repository and returns a new
// UsernamePasswordCredential containing the credential's PublicId. c is not
// changed. c must not contain a PublicId. The PublicId is generated and
// assigned by this method. c must contain a valid StoreId.
//
// The password is encrypted and a HmacSha256 of the password is calculated. Only the
// PasswordHmac is returned, the plain-text and encrypted password is not returned.
//
// Both c.Name and c.Description are optional. If c.Name is set, it must
// be unique within c.ScopeId. Both c.CreateTime and c.UpdateTime are
// ignored.
func (r *Repository) CreateUsernamePasswordCredential(
ctx context.Context,
scopeId string,
c *UsernamePasswordCredential,
_ ...Option,
) (*UsernamePasswordCredential, error) {
const op = "static.(Repository).CreateUsernamePasswordCredential"
if c == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing credential")
}
if c.UsernamePasswordCredential == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing embedded credential")
}
if scopeId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id")
}
if c.Username == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing username")
}
if c.Password == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing password")
}
if c.StoreId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing store id")
}
if c.PublicId != "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
}
c = c.clone()
id, err := newCredentialId(ctx)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
c.PublicId = id
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
// encrypt
databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
}
if err := c.encrypt(ctx, databaseWrapper); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
var newCred *UsernamePasswordCredential
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
func(_ db.Reader, w db.Writer) error {
newCred = c.clone()
if err := w.Create(ctx, newCred,
db.WithOplog(oplogWrapper, newCred.oplog(oplog.OpType_OP_TYPE_CREATE))); err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
},
)
if err != nil {
if errors.IsUniqueError(err) {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in store: %s: name %s already exists", c.StoreId, c.Name)))
}
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in store: %s", c.StoreId)))
}
// Clear password fields, only passwordHmac should be returned
newCred.CtPassword = nil
newCred.Password = nil
return newCred, nil
}
// LookupCredential returns the Credential for the publicId. Returns
// nil, nil if no Credential is found for the publicId.
// TODO: This should hit a view and return the interface type...
func (r *Repository) LookupCredential(ctx context.Context, publicId string, _ ...Option) (*UsernamePasswordCredential, error) {
const op = "static.(Repository).LookupCredential"
if publicId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
}
cred := allocUsernamePasswordCredential()
cred.PublicId = publicId
if err := r.reader.LookupByPublicId(ctx, cred); err != nil {
if errors.IsNotFoundError(err) {
return nil, nil
}
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", publicId)))
}
// Clear password fields, only passwordHmac should be returned
cred.CtPassword = nil
cred.Password = nil
return cred, nil
}
// UpdateUsernamePasswordCredential updates the repository entry for c.PublicId with
// the values in c for the fields listed in fieldMaskPaths. It returns a
// new UsernamePasswordCredential containing the updated values and a count of the
// number of records updated. c is not changed.
//
// c must contain a valid PublicId. Only Name, Description, Username and Password can be
// changed. If c.Name is set to a non-empty string, it must be unique within c.ScopeId.
//
// An attribute of c will be set to NULL in the database if the attribute
// in c is the zero value and it is included in fieldMaskPaths.
func (r *Repository) UpdateUsernamePasswordCredential(ctx context.Context,
scopeId string,
c *UsernamePasswordCredential,
version uint32,
fieldMaskPaths []string,
_ ...Option,
) (*UsernamePasswordCredential, int, error) {
const op = "static.(Repository).UpdateUsernamePasswordCredential"
if c == nil {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing Credential")
}
if c.UsernamePasswordCredential == nil {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing embedded Credential")
}
if c.PublicId == "" {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id")
}
if version == 0 {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version")
}
if scopeId == "" {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing scope id")
}
if c.StoreId == "" {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing store id")
}
c = c.clone()
for _, f := range fieldMaskPaths {
switch {
case strings.EqualFold(nameField, f):
case strings.EqualFold(descriptionField, f):
case strings.EqualFold(usernameField, f):
case strings.EqualFold(passwordField, f):
default:
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, f)
}
}
dbMask, nullFields := dbcommon.BuildUpdatePaths(
map[string]interface{}{
nameField: c.Name,
descriptionField: c.Description,
usernameField: c.Username,
passwordField: c.Password,
},
fieldMaskPaths,
nil,
)
if len(dbMask) == 0 && len(nullFields) == 0 {
return nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "missing field mask")
}
for _, f := range fieldMaskPaths {
if strings.EqualFold(passwordField, f) {
// Password has been updated, re-encrypt and recalculate hmac
databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
if err != nil {
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
}
if err := c.encrypt(ctx, databaseWrapper); err != nil {
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op)
}
// Set PasswordHmac and CtPassword masks for update.
dbMask = append(dbMask, "PasswordHmac", "CtPassword")
}
}
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, db.NoRowsAffected,
errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var rowsUpdated int
var returnedCredential *UsernamePasswordCredential
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
func(_ db.Reader, w db.Writer) error {
returnedCredential = c.clone()
var err error
rowsUpdated, err = w.Update(ctx, returnedCredential,
dbMask, nullFields,
db.WithOplog(oplogWrapper, returnedCredential.oplog(oplog.OpType_OP_TYPE_UPDATE)),
db.WithVersion(&version))
if err != nil {
return errors.Wrap(ctx, err, op)
}
if rowsUpdated > 1 {
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated")
}
return nil
},
)
if err != nil {
return nil, db.NoRowsAffected, err
}
// Clear password fields, only passwordHmac should be returned
returnedCredential.CtPassword = nil
returnedCredential.Password = nil
return returnedCredential, rowsUpdated, nil
}
// ListCredentials returns a slice of UsernamePasswordCredentials for the
// scopeIds. WithLimit is the only option supported.
// TODO: This should hit a view and return the interface type...
func (r *Repository) ListCredentials(ctx context.Context, storeId string, opt ...Option) ([]*UsernamePasswordCredential, error) {
const op = "static.(Repository).ListCredentials"
if storeId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "no storeId")
}
opts := getOpts(opt...)
limit := r.defaultLimit
if opts.withLimit != 0 {
// non-zero signals an override of the default limit for the repo.
limit = opts.withLimit
}
var creds []*UsernamePasswordCredential
err := r.reader.SearchWhere(ctx, &creds, "store_id = ?", []interface{}{storeId}, db.WithLimit(limit))
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
for _, c := range creds {
// Clear password fields, only passwordHmac should be returned
c.CtPassword = nil
c.Password = nil
}
return creds, nil
}
// DeleteCredential deletes publicId from the repository and returns
// the number of records deleted. All options are ignored.
// TODO: This should hit a view...
func (r *Repository) DeleteCredential(ctx context.Context, scopeId, id string, _ ...Option) (int, error) {
const op = "static.(Repository).DeleteCredential"
if id == "" {
return db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id")
}
if scopeId == "" {
return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no scope id")
}
c := allocUsernamePasswordCredential()
c.PublicId = id
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
if err != nil {
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get oplog wrapper"))
}
var rowsDeleted int
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(_ db.Reader, w db.Writer) (err error) {
rowsDeleted, err = w.Delete(ctx, c, db.WithOplog(oplogWrapper, c.oplog(oplog.OpType_OP_TYPE_DELETE)))
if err != nil {
return errors.Wrap(ctx, err, op)
}
if rowsDeleted > 1 {
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been deleted")
}
return nil
},
)
if err != nil {
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(id))
}
return rowsDeleted, nil
}
@@ -0,0 +1,237 @@
package static
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/db"
dbcommon "github.com/hashicorp/boundary/internal/db/common"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
)
// CreateCredentialStore inserts cs into the repository and returns a new
// CredentialStore containing the credential store's PublicId. cs is not
// changed. cs must not contain a PublicId. The PublicId is generated and
// assigned by this method. cs must contain a valid ScopeId.
//
// Both cs.Name and cs.Description are optional. If cs.Name is set, it must
// be unique within cs.ScopeId. Both cs.CreateTime and cs.UpdateTime are
// ignored.
func (r *Repository) CreateCredentialStore(ctx context.Context, cs *CredentialStore, _ ...Option) (*CredentialStore, error) {
const op = "static.(Repository).CreateCredentialStore"
if cs == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing CredentialStore")
}
if cs.CredentialStore == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing embedded CredentialStore")
}
if cs.ScopeId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id")
}
if cs.PublicId != "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
}
cs = cs.clone()
id, err := newCredentialStoreId(ctx)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
cs.PublicId = id
oplogWrapper, err := r.kms.GetWrapper(ctx, cs.ScopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var newCredentialStore *CredentialStore
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
func(_ db.Reader, w db.Writer) error {
newCredentialStore = cs.clone()
if err := w.Create(ctx, newCredentialStore,
db.WithOplog(oplogWrapper, newCredentialStore.oplog(oplog.OpType_OP_TYPE_CREATE))); err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
},
)
if err != nil {
if errors.IsUniqueError(err) {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s: name %s already exists", cs.ScopeId, cs.Name)))
}
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s", cs.ScopeId)))
}
return newCredentialStore, nil
}
// LookupCredentialStore returns the CredentialStore for publicId. Returns
// nil, nil if no CredentialStore is found for publicId.
func (r *Repository) LookupCredentialStore(ctx context.Context, publicId string, _ ...Option) (*CredentialStore, error) {
const op = "static.(Repository).LookupCredentialStore"
if publicId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
}
cs := allocCredentialStore()
cs.PublicId = publicId
if err := r.reader.LookupByPublicId(ctx, cs); err != nil {
if errors.IsNotFoundError(err) {
return nil, nil
}
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", publicId)))
}
return cs, nil
}
// UpdateCredentialStore updates the repository entry for cs.PublicId with
// the values in cs for the fields listed in fieldMaskPaths. It returns a
// new CredentialStore containing the updated values and a count of the
// number of records updated. cs is not changed.
//
// cs must contain a valid PublicId. Only Name and Description can be changed. If cs.Name
// is set to a non-empty string, it must be unique within cs.ScopeId.
//
// An attribute of cs will be set to NULL in the database if the attribute
// in cs is the zero value and it is included in fieldMaskPaths.
func (r *Repository) UpdateCredentialStore(ctx context.Context, cs *CredentialStore, version uint32, fieldMaskPaths []string, _ ...Option) (*CredentialStore, int, error) {
const op = "static.(Repository).UpdateCredentialStore"
if cs == nil {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing CredentialStore")
}
if cs.CredentialStore == nil {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing embedded CredentialStore")
}
if cs.PublicId == "" {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id")
}
if version == 0 {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version")
}
if cs.ScopeId == "" {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing scope id")
}
cs = cs.clone()
for _, f := range fieldMaskPaths {
switch {
case strings.EqualFold(nameField, f):
case strings.EqualFold(descriptionField, f):
default:
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, f)
}
}
dbMask, nullFields := dbcommon.BuildUpdatePaths(
map[string]interface{}{
nameField: cs.Name,
descriptionField: cs.Description,
},
fieldMaskPaths,
nil,
)
if len(dbMask) == 0 && len(nullFields) == 0 {
return nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "missing field mask")
}
oplogWrapper, err := r.kms.GetWrapper(ctx, cs.ScopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, db.NoRowsAffected,
errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var rowsUpdated int
var returnedCredentialStore *CredentialStore
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
func(_ db.Reader, w db.Writer) error {
returnedCredentialStore = cs.clone()
var err error
rowsUpdated, err = w.Update(ctx, returnedCredentialStore,
dbMask, nullFields,
db.WithOplog(oplogWrapper, returnedCredentialStore.oplog(oplog.OpType_OP_TYPE_UPDATE)),
db.WithVersion(&version))
if err != nil {
return errors.Wrap(ctx, err, op)
}
if rowsUpdated > 1 {
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated")
}
return nil
},
)
if err != nil {
return nil, db.NoRowsAffected, err
}
return returnedCredentialStore, rowsUpdated, nil
}
// ListCredentialStores returns a slice of CredentialStores for the
// scopeIds. WithLimit is the only option supported.
func (r *Repository) ListCredentialStores(ctx context.Context, scopeIds []string, opt ...Option) ([]*CredentialStore, error) {
const op = "static.(Repository).ListCredentialStores"
if len(scopeIds) == 0 {
return nil, errors.New(ctx, errors.InvalidParameter, op, "no scopeIds")
}
opts := getOpts(opt...)
limit := r.defaultLimit
if opts.withLimit != 0 {
// non-zero signals an override of the default limit for the repo.
limit = opts.withLimit
}
var credentialStores []*CredentialStore
err := r.reader.SearchWhere(ctx, &credentialStores, "scope_id in (?)", []interface{}{scopeIds}, db.WithLimit(limit))
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
return credentialStores, nil
}
// DeleteCredentialStore deletes publicId from the repository and returns
// the number of records deleted. All options are ignored.
func (r *Repository) DeleteCredentialStore(ctx context.Context, publicId string, _ ...Option) (int, error) {
const op = "static.(Repository).DeleteCredentialStore"
if publicId == "" {
return db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id")
}
cs := allocCredentialStore()
cs.PublicId = publicId
if err := r.reader.LookupByPublicId(ctx, cs); err != nil {
if errors.IsNotFoundError(err) {
return db.NoRowsAffected, nil
}
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId)))
}
if cs.ScopeId == "" {
return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no scope id")
}
oplogWrapper, err := r.kms.GetWrapper(ctx, cs.ScopeId, kms.KeyPurposeOplog)
if err != nil {
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get oplog wrapper"))
}
var rowsDeleted int
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(_ db.Reader, w db.Writer) (err error) {
rowsDeleted, err = w.Delete(ctx, cs, db.WithOplog(oplogWrapper, cs.oplog(oplog.OpType_OP_TYPE_DELETE)))
if err != nil {
return errors.Wrap(ctx, err, op)
}
if rowsDeleted > 1 {
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been deleted")
}
return nil
},
)
if err != nil {
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(publicId))
}
return rowsDeleted, nil
}
@@ -0,0 +1,746 @@
package static
import (
"context"
"strings"
"testing"
"time"
"github.com/hashicorp/boundary/internal/credential/static/store"
"github.com/hashicorp/boundary/internal/db"
dbassert "github.com/hashicorp/boundary/internal/db/assert"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func assertPublicId(t *testing.T, prefix, actual string) {
t.Helper()
assert.NotEmpty(t, actual)
parts := strings.Split(actual, "_")
assert.Equalf(t, 2, len(parts), "want one '_' in PublicId, got multiple in %q", actual)
assert.Equalf(t, prefix, parts[0], "PublicId want prefix: %q, got: %q in %q", prefix, parts[0], actual)
}
func TestRepository_CreateCredentialStore(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
tests := []struct {
name string
store *CredentialStore
wantErr bool
wantErrCode errors.Code
}{
{
name: "missing-store",
wantErr: true,
wantErrCode: errors.InvalidParameter,
},
{
name: "missing-embedded-store",
store: &CredentialStore{},
wantErr: true,
wantErrCode: errors.InvalidParameter,
},
{
name: "missing-scope-id",
store: &CredentialStore{
CredentialStore: &store.CredentialStore{},
},
wantErr: true,
wantErrCode: errors.InvalidParameter,
},
{
name: "public-id-set",
store: &CredentialStore{
CredentialStore: &store.CredentialStore{
ScopeId: prj.PublicId,
PublicId: "bad-dont-set-this",
},
},
wantErr: true,
wantErrCode: errors.InvalidParameter,
},
{
name: "valid",
store: &CredentialStore{
CredentialStore: &store.CredentialStore{
ScopeId: prj.PublicId,
},
},
},
{
name: "valid-with-name",
store: &CredentialStore{
CredentialStore: &store.CredentialStore{
ScopeId: prj.PublicId,
Name: "test-store",
},
},
},
{
name: "valid-with-description",
store: &CredentialStore{
CredentialStore: &store.CredentialStore{
ScopeId: prj.PublicId,
Description: "test-store-description",
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms)
require.NoError(err)
require.NotNil(repo)
got, err := repo.CreateCredentialStore(ctx, tt.store)
if tt.wantErr {
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
assert.Nil(got)
return
}
require.NoError(err)
assert.Empty(tt.store.PublicId)
require.NotNil(got)
assertPublicId(t, CredentialStorePrefix, got.PublicId)
assert.NotSame(tt.store, got)
assert.Equal(got.CreateTime, got.UpdateTime)
assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)))
})
}
t.Run("duplicate-names", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms)
require.NoError(err)
require.NotNil(repo)
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NoError(err)
in, err := NewCredentialStore(prj.GetPublicId(), WithName("my-name"), WithDescription("desc"))
assert.NoError(err)
got, err := repo.CreateCredentialStore(ctx, in)
require.NoError(err)
assert.Equal(in.Name, got.Name)
assert.Equal(in.Description, got.Description)
in2, err := NewCredentialStore(prj.GetPublicId(), WithName("my-name"), WithDescription("desc"))
got2, err := repo.CreateCredentialStore(ctx, in2)
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err)
assert.Nil(got2)
// Creating credential in different scope should not conflict
in3, err := NewCredentialStore(org.GetPublicId(), WithName("my-name"), WithDescription("desc"))
got3, err := repo.CreateCredentialStore(ctx, in3)
require.NoError(err)
assert.Equal(in.Name, got3.Name)
assert.Equal(in3.Name, got3.Name)
assert.Equal(in3.Description, got3.Description)
assert.NotEqual(got.PublicId, got3.PublicId)
})
}
func TestRepository_LookupCredentialStore(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
store := TestCredentialStore(t, conn, wrapper, prj.PublicId)
tests := []struct {
name string
id string
want *CredentialStore
wantErr errors.Code
}{
{
name: "valid-with-client-cert",
id: store.GetPublicId(),
want: store,
},
{
name: "empty-public-id",
id: "",
wantErr: errors.InvalidParameter,
},
{
name: "not-found",
id: "cs_fake",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
got, err := repo.LookupCredentialStore(ctx, tt.id)
if tt.wantErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
assert.Nil(got)
return
}
require.NoError(err)
if tt.want == nil {
assert.Nil(got)
return
}
assert.NotNil(got)
assert.Equal(got, tt.want)
})
}
}
func TestRepository_UpdateCredentialStore(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
changeName := func(n string) func(*CredentialStore) *CredentialStore {
return func(cs *CredentialStore) *CredentialStore {
cs.Name = n
return cs
}
}
changeDescription := func(d string) func(*CredentialStore) *CredentialStore {
return func(cs *CredentialStore) *CredentialStore {
cs.Description = d
return cs
}
}
makeNil := func() func(*CredentialStore) *CredentialStore {
return func(cs *CredentialStore) *CredentialStore {
return nil
}
}
makeEmbeddedNil := func() func(*CredentialStore) *CredentialStore {
return func(cs *CredentialStore) *CredentialStore {
return &CredentialStore{}
}
}
deletePublicId := func() func(*CredentialStore) *CredentialStore {
return func(cs *CredentialStore) *CredentialStore {
cs.PublicId = ""
return cs
}
}
nonExistentPublicId := func() func(*CredentialStore) *CredentialStore {
return func(cs *CredentialStore) *CredentialStore {
cs.PublicId = "abcd_OOOOOOOOOO"
return cs
}
}
combine := func(fns ...func(cs *CredentialStore) *CredentialStore) func(*CredentialStore) *CredentialStore {
return func(cs *CredentialStore) *CredentialStore {
for _, fn := range fns {
cs = fn(cs)
}
return cs
}
}
tests := []struct {
name string
orig *CredentialStore
chgFn func(*CredentialStore) *CredentialStore
masks []string
want *CredentialStore
wantCount int
wantErr errors.Code
}{
{
name: "nil-credential-store",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{},
},
chgFn: makeNil(),
masks: []string{"Name", "Description"},
wantErr: errors.InvalidParameter,
},
{
name: "nil-embedded-credential-store",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{},
},
chgFn: makeEmbeddedNil(),
masks: []string{"Name", "Description"},
wantErr: errors.InvalidParameter,
},
{
name: "no-public-id",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{},
},
chgFn: deletePublicId(),
masks: []string{"Name", "Description"},
wantErr: errors.InvalidPublicId,
},
{
name: "updating-non-existent-credential-store",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
},
},
chgFn: combine(nonExistentPublicId(), changeName("test-update-name-repo")),
masks: []string{"Name"},
wantErr: errors.RecordNotFound,
},
{
name: "empty-field-mask",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
},
},
chgFn: changeName("test-update-name-repo"),
wantErr: errors.EmptyFieldMask,
},
{
name: "read-only-fields-in-field-mask",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
},
},
chgFn: changeName("test-update-name-repo"),
masks: []string{"PublicId", "CreateTime", "UpdateTime", "ScopeId"},
wantErr: errors.InvalidFieldMask,
},
{
name: "unknown-field-in-field-mask",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
},
},
chgFn: changeName("test-update-name-repo"),
masks: []string{"Bilbo"},
wantErr: errors.InvalidFieldMask,
},
{
name: "change-name",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
},
},
chgFn: changeName("test-update-name-repo"),
masks: []string{"Name"},
want: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-update-name-repo",
},
},
wantCount: 1,
},
{
name: "change-description",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Description: "test-description-repo",
},
},
chgFn: changeDescription("test-update-description-repo"),
masks: []string{"Description"},
want: &CredentialStore{
CredentialStore: &store.CredentialStore{
Description: "test-update-description-repo",
},
},
wantCount: 1,
},
{
name: "change-name-and-description",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
Description: "test-description-repo",
},
},
chgFn: combine(changeDescription("test-update-description-repo"), changeName("test-update-name-repo")),
masks: []string{"Name", "Description"},
want: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-update-name-repo",
Description: "test-update-description-repo",
},
},
wantCount: 1,
},
{
name: "delete-name",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
Description: "test-description-repo",
},
},
masks: []string{"Name"},
chgFn: combine(changeDescription("test-update-description-repo"), changeName("")),
want: &CredentialStore{
CredentialStore: &store.CredentialStore{
Description: "test-description-repo",
},
},
wantCount: 1,
},
{
name: "delete-description",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
Description: "test-description-repo",
},
},
masks: []string{"Description"},
chgFn: combine(changeDescription(""), changeName("test-update-name-repo")),
want: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
},
},
wantCount: 1,
},
{
name: "do-not-delete-name",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
Description: "test-description-repo",
},
},
masks: []string{"Description"},
chgFn: combine(changeDescription("test-update-description-repo"), changeName("")),
want: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
Description: "test-update-description-repo",
},
},
wantCount: 1,
},
{
name: "do-not-delete-description",
orig: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
Description: "test-description-repo",
},
},
masks: []string{"Name"},
chgFn: combine(changeDescription(""), changeName("test-update-name-repo")),
want: &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-update-name-repo",
Description: "test-description-repo",
},
},
wantCount: 1,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
tt.orig.ScopeId = prj.GetPublicId()
orig, err := repo.CreateCredentialStore(ctx, tt.orig)
assert.NoError(err)
require.NotNil(orig)
if tt.chgFn != nil {
orig = tt.chgFn(orig)
}
got, gotCount, err := repo.UpdateCredentialStore(ctx, orig, 1, tt.masks)
if tt.wantErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
assert.Equal(tt.wantCount, gotCount, "row count")
assert.Nil(got)
return
}
assert.NoError(err)
assert.Empty(tt.orig.PublicId)
require.NotNil(got)
assertPublicId(t, CredentialStorePrefix, got.PublicId)
assert.Equal(tt.wantCount, gotCount, "row count")
assert.NotSame(tt.orig, got)
assert.Equal(tt.orig.ScopeId, got.ScopeId)
underlyingDB, err := conn.SqlDB(ctx)
require.NoError(err)
dbassert := dbassert.New(t, underlyingDB)
if tt.want.Name == "" {
dbassert.IsNull(got, "name")
} else {
assert.Equal(tt.want.Name, got.Name)
}
if tt.want.Description == "" {
dbassert.IsNull(got, "description")
} else {
assert.Equal(tt.want.Description, got.Description)
}
if tt.wantCount > 0 {
assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)))
}
})
}
t.Run("invalid-duplicate-names", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
require.NoError(err)
name := "test-dup-name"
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
css := TestCredentialStores(t, conn, wrapper, prj.PublicId, 2)
csA, csB := css[0], css[1]
csA.Name = name
got1, gotCount1, err := repo.UpdateCredentialStore(ctx, csA, 1, []string{"Name"})
assert.NoError(err)
require.NotNil(got1)
assert.Equal(name, got1.Name)
assert.Equal(1, gotCount1, "row count")
assert.NoError(db.TestVerifyOplog(t, rw, csA.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)))
csB.Name = name
got2, gotCount2, err := repo.UpdateCredentialStore(ctx, csB, 1, []string{"Name"})
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err)
assert.Nil(got2)
assert.Equal(db.NoRowsAffected, gotCount2, "row count")
})
t.Run("valid-duplicate-names-diff-scopes", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
in := &CredentialStore{
CredentialStore: &store.CredentialStore{
Name: "test-name-repo",
},
}
in2 := in.clone()
in.ScopeId = prj.GetPublicId()
got, err := repo.CreateCredentialStore(ctx, in)
assert.NoError(err)
require.NotNil(got)
assertPublicId(t, CredentialStorePrefix, got.PublicId)
assert.NotSame(in, got)
assert.Equal(in.Name, got.Name)
assert.Equal(in.Description, got.Description)
in2.ScopeId = org.GetPublicId()
in2.Name = "first-name"
got2, err := repo.CreateCredentialStore(ctx, in2)
assert.NoError(err)
require.NotNil(got2)
got2.Name = got.Name
got3, gotCount3, err := repo.UpdateCredentialStore(ctx, got2, 1, []string{"Name"})
assert.NoError(err)
require.NotNil(got3)
assert.NotSame(got2, got3)
assert.Equal(got.Name, got3.Name)
assert.Equal(got2.Description, got3.Description)
assert.Equal(1, gotCount3, "row count")
})
t.Run("change-scope-id", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
iamRepo := iam.TestRepo(t, conn, wrapper)
_, prj1 := iam.TestScopes(t, iamRepo)
_, prj2 := iam.TestScopes(t, iamRepo)
csA, csB := TestCredentialStores(t, conn, wrapper, prj1.PublicId, 1)[0], TestCredentialStores(t, conn, wrapper, prj2.PublicId, 1)[0]
assert.NotEqual(csA.ScopeId, csB.ScopeId)
orig := csA.clone()
csA.ScopeId = csB.ScopeId
assert.Equal(csA.ScopeId, csB.ScopeId)
got1, gotCount1, err := repo.UpdateCredentialStore(ctx, csA, 1, []string{"Name"})
assert.NoError(err)
require.NotNil(got1)
assert.Equal(orig.ScopeId, got1.ScopeId)
assert.Equal(1, gotCount1, "row count")
})
}
func TestRepository_ListCredentialStores(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(context.Background(), rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
const num = 10
var prjs []string
var total int
for i := 0; i < num; i++ {
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
prjs = append(prjs, prj.GetPublicId())
TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), num)
total += num
}
type args struct {
scopeIds []string
opt []Option
}
tests := []struct {
name string
args args
wantCnt int
}{
{
name: "no-limit",
args: args{
scopeIds: prjs,
opt: []Option{WithLimit(-1)},
},
wantCnt: total,
},
{
name: "default-limit",
args: args{
scopeIds: prjs,
},
wantCnt: total,
},
{
name: "custom-limit",
args: args{
scopeIds: prjs,
opt: []Option{WithLimit(3)},
},
wantCnt: 3,
},
{
name: "bad-scope",
args: args{
scopeIds: []string{"bad-id"},
},
wantCnt: 0,
},
}
for _, tt := range tests {
got, err := repo.ListCredentialStores(context.Background(), tt.args.scopeIds, tt.args.opt...)
require.NoError(err)
assert.Equal(tt.wantCnt, len(got))
}
}
func TestRepository_DeleteCredentialStore(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
iam.TestRepo(t, conn, wrapper)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
store := TestCredentialStore(t, conn, wrapper, prj.PublicId)
tests := []struct {
name string
in string
want int
wantErr bool
wantErrCode errors.Code
}{
{
name: "With no public id",
wantErr: true,
wantErrCode: errors.InvalidPublicId,
},
{
name: "With non existing account id",
in: "cs_fakeid",
want: 0,
},
{
name: "With existing account id",
in: store.GetPublicId(),
want: 1,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(context.Background(), rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
got, err := repo.DeleteCredentialStore(context.Background(), tt.in)
if tt.wantErr {
assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "Unexpected error %s", err)
return
}
require.NoError(err)
assert.EqualValues(tt.want, got)
})
}
}
@@ -0,0 +1,896 @@
package static
import (
"context"
"testing"
"time"
"github.com/hashicorp/boundary/internal/credential/static/store"
"github.com/hashicorp/boundary/internal/db"
dbassert "github.com/hashicorp/boundary/internal/db/assert"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepository_CreateUsernamePasswordCredential(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cs := TestCredentialStore(t, conn, wrapper, prj.PublicId)
tests := []struct {
name string
scopeId string
cred *UsernamePasswordCredential
wantErr bool
wantErrCode errors.Code
}{
{
name: "missing-store",
wantErr: true,
wantErrCode: errors.InvalidParameter,
},
{
name: "missing-embedded-cred",
cred: &UsernamePasswordCredential{},
wantErr: true,
wantErrCode: errors.InvalidParameter,
},
{
name: "missing-scope-id",
cred: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "my-user",
Password: []byte("secret"),
StoreId: cs.PublicId,
},
},
wantErr: true,
wantErrCode: errors.InvalidParameter,
},
{
name: "missing-username",
scopeId: prj.PublicId,
cred: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Password: []byte("secret"),
StoreId: cs.PublicId,
},
},
wantErr: true,
wantErrCode: errors.InvalidParameter,
},
{
name: "missing-password",
scopeId: prj.PublicId,
cred: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "my-user",
StoreId: cs.PublicId,
},
},
wantErr: true,
wantErrCode: errors.InvalidParameter,
},
{
name: "missing-store-id",
scopeId: prj.PublicId,
cred: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "my-user",
Password: []byte("secret"),
},
},
wantErr: true,
wantErrCode: errors.InvalidParameter,
},
{
name: "valid",
scopeId: prj.PublicId,
cred: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "my-user",
Password: []byte("secret"),
StoreId: cs.PublicId,
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kkms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kkms)
require.NoError(err)
require.NotNil(repo)
got, err := repo.CreateUsernamePasswordCredential(ctx, tt.scopeId, tt.cred)
if tt.wantErr {
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
assert.Nil(got)
return
}
require.NoError(err)
assertPublicId(t, CredentialPrefix, got.PublicId)
assert.Equal(tt.cred.Username, got.Username)
assert.Nil(got.Password)
assert.Nil(got.CtPassword)
// Validate password
lookupCred := allocUsernamePasswordCredential()
lookupCred.PublicId = got.PublicId
require.NoError(rw.LookupById(ctx, lookupCred))
databaseWrapper, err := kkms.GetWrapper(context.Background(), tt.scopeId, kms.KeyPurposeDatabase)
require.NoError(err)
require.NoError(lookupCred.decrypt(ctx, databaseWrapper))
assert.Equal(tt.cred.Password, lookupCred.Password)
assert.Empty(got.Password)
assert.Empty(got.CtPassword)
assert.NotEmpty(got.PasswordHmac)
// Validate hmac
hm, err := crypto.HmacSha256(ctx, tt.cred.Password, databaseWrapper, []byte(tt.cred.StoreId), nil, crypto.WithEd25519())
require.NoError(err)
assert.Equal([]byte(hm), got.PasswordHmac)
// Validate oplog
assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)))
})
}
t.Run("duplicate-names", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms)
require.NoError(err)
require.NotNil(repo)
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NoError(err)
prjCs := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
orgCs := TestCredentialStore(t, conn, wrapper, org.GetPublicId())
in, err := NewUsernamePasswordCredential(prjCs.GetPublicId(), "user", "pass", WithName("my-name"), WithDescription("original"))
assert.NoError(err)
got, err := repo.CreateUsernamePasswordCredential(ctx, prj.PublicId, in)
require.NoError(err)
assert.Equal(in.Name, got.Name)
assert.Equal(in.Description, got.Description)
in2, err := NewUsernamePasswordCredential(prjCs.GetPublicId(), "user", "pass", WithName("my-name"), WithDescription("different"))
got2, err := repo.CreateUsernamePasswordCredential(ctx, prj.GetPublicId(), in2)
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err)
assert.Nil(got2)
// Creating credential in different scope should not conflict
in3, err := NewUsernamePasswordCredential(orgCs.GetPublicId(), "user", "pass", WithName("my-name"), WithDescription("different"))
got3, err := repo.CreateUsernamePasswordCredential(ctx, org.GetPublicId(), in3)
require.NoError(err)
assert.Equal(in3.Name, got3.Name)
assert.Equal(in3.Description, got3.Description)
assert.NotEqual(got.PublicId, got3.PublicId)
})
}
func TestRepository_LookupCredential(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
store := TestCredentialStore(t, conn, wrapper, prj.PublicId)
cred := TestUsernamePasswordCredential(t, conn, wrapper, "username", "password", store.PublicId, prj.PublicId)
tests := []struct {
name string
id string
want *UsernamePasswordCredential
wantErr errors.Code
}{
{
name: "valid-with-client-cert",
id: cred.GetPublicId(),
want: cred,
},
{
name: "empty-public-id",
id: "",
wantErr: errors.InvalidParameter,
},
{
name: "not-found",
id: "cred_fake",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
got, err := repo.LookupCredential(ctx, tt.id)
if tt.wantErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
assert.Nil(got)
return
}
require.NoError(err)
if tt.want == nil {
assert.Nil(got)
return
}
assert.NotNil(got)
assert.Empty(got.Password)
assert.Empty(got.CtPassword)
assert.NotEmpty(got.PasswordHmac)
})
}
}
func TestRepository_ListCredentials(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(context.Background(), rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
total := 10
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
store := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId(), total)
type args struct {
storeId string
opt []Option
}
tests := []struct {
name string
args args
wantCnt int
}{
{
name: "no-limit",
args: args{
storeId: store.PublicId,
opt: []Option{WithLimit(-1)},
},
wantCnt: total,
},
{
name: "default-limit",
args: args{
storeId: store.PublicId,
},
wantCnt: total,
},
{
name: "custom-limit",
args: args{
storeId: store.PublicId,
opt: []Option{WithLimit(3)},
},
wantCnt: 3,
},
{
name: "bad-store",
args: args{
storeId: "bad-id",
},
wantCnt: 0,
},
}
for _, tt := range tests {
got, err := repo.ListCredentials(context.Background(), tt.args.storeId, tt.args.opt...)
require.NoError(err)
assert.Equal(tt.wantCnt, len(got))
// Validate only passwordHmac is returned
for _, c := range got {
assert.Empty(c.Password)
assert.Empty(c.CtPassword)
assert.NotEmpty(c.PasswordHmac)
}
}
}
func TestRepository_DeleteCredential(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
iam.TestRepo(t, conn, wrapper)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
store := TestCredentialStore(t, conn, wrapper, prj.PublicId)
cred := TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId())
tests := []struct {
name string
in string
want int
wantErr bool
wantErrCode errors.Code
}{
{
name: "With no public id",
wantErr: true,
wantErrCode: errors.InvalidPublicId,
},
{
name: "With non existing account id",
in: "cred_fakeid",
want: 0,
},
{
name: "With existing id",
in: cred.GetPublicId(),
want: 1,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(context.Background(), rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
got, err := repo.DeleteCredential(context.Background(), prj.GetPublicId(), tt.in)
if tt.wantErr {
assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "Unexpected error %s", err)
return
}
require.NoError(err)
assert.EqualValues(tt.want, got)
})
}
}
func TestRepository_UpdateUsernamePasswordCredential(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
changeName := func(n string) func(credential *UsernamePasswordCredential) *UsernamePasswordCredential {
return func(c *UsernamePasswordCredential) *UsernamePasswordCredential {
c.Name = n
return c
}
}
changeDescription := func(d string) func(*UsernamePasswordCredential) *UsernamePasswordCredential {
return func(c *UsernamePasswordCredential) *UsernamePasswordCredential {
c.Description = d
return c
}
}
makeNil := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential {
return func(_ *UsernamePasswordCredential) *UsernamePasswordCredential {
return nil
}
}
makeEmbeddedNil := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential {
return func(_ *UsernamePasswordCredential) *UsernamePasswordCredential {
return &UsernamePasswordCredential{}
}
}
deletePublicId := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential {
return func(c *UsernamePasswordCredential) *UsernamePasswordCredential {
c.PublicId = ""
return c
}
}
deleteStoreId := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential {
return func(c *UsernamePasswordCredential) *UsernamePasswordCredential {
c.StoreId = ""
return c
}
}
deleteVersion := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential {
return func(c *UsernamePasswordCredential) *UsernamePasswordCredential {
c.Version = 0
return c
}
}
nonExistentPublicId := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential {
return func(c *UsernamePasswordCredential) *UsernamePasswordCredential {
c.PublicId = "abcd_OOOOOOOOOO"
return c
}
}
changeUser := func(n string) func(credential *UsernamePasswordCredential) *UsernamePasswordCredential {
return func(c *UsernamePasswordCredential) *UsernamePasswordCredential {
c.Username = n
return c
}
}
changePassword := func(d string) func(*UsernamePasswordCredential) *UsernamePasswordCredential {
return func(c *UsernamePasswordCredential) *UsernamePasswordCredential {
c.Password = []byte(d)
return c
}
}
combine := func(fns ...func(cs *UsernamePasswordCredential) *UsernamePasswordCredential) func(*UsernamePasswordCredential) *UsernamePasswordCredential {
return func(c *UsernamePasswordCredential) *UsernamePasswordCredential {
for _, fn := range fns {
c = fn(c)
}
return c
}
}
tests := []struct {
name string
orig *UsernamePasswordCredential
chgFn func(*UsernamePasswordCredential) *UsernamePasswordCredential
masks []string
want *UsernamePasswordCredential
wantCount int
wantErr errors.Code
}{
{
name: "nil-credential",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("pass"),
},
},
chgFn: makeNil(),
masks: []string{"Name", "Description"},
wantErr: errors.InvalidParameter,
},
{
name: "nil-embedded-credential",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("pass"),
},
},
chgFn: makeEmbeddedNil(),
masks: []string{"Name", "Description"},
wantErr: errors.InvalidParameter,
},
{
name: "no-public-id",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("pass"),
},
},
chgFn: deletePublicId(),
masks: []string{"Name", "Description"},
wantErr: errors.InvalidPublicId,
},
{
name: "no-store-id",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("pass"),
},
},
chgFn: deleteStoreId(),
masks: []string{"Name", "Description"},
wantErr: errors.InvalidParameter,
},
{
name: "no-version",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("pass"),
},
},
chgFn: deleteVersion(),
masks: []string{"Name", "Description"},
wantErr: errors.InvalidParameter,
},
{
name: "updating-non-existent-credential",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Username: "user",
Password: []byte("pass"),
},
},
chgFn: combine(nonExistentPublicId(), changeName("test-update-name-repo")),
masks: []string{"Name"},
wantErr: errors.RecordNotFound,
},
{
name: "empty-field-mask",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Username: "user",
Password: []byte("pass"),
},
},
chgFn: changeName("test-update-name-repo"),
wantErr: errors.EmptyFieldMask,
},
{
name: "read-only-fields-in-field-mask",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Username: "user",
Password: []byte("pass"),
},
},
chgFn: changeName("test-update-name-repo"),
masks: []string{"PublicId", "CreateTime", "UpdateTime", "ScopeId"},
wantErr: errors.InvalidFieldMask,
},
{
name: "unknown-field-in-field-mask",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Username: "user",
Password: []byte("pass"),
},
},
chgFn: changeName("test-update-name-repo"),
masks: []string{"Bilbo"},
wantErr: errors.InvalidFieldMask,
},
{
name: "change-name",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Username: "user",
Password: []byte("pass"),
},
},
chgFn: changeName("test-update-name-repo"),
masks: []string{"Name"},
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-update-name-repo",
Username: "user",
Password: []byte("pass"),
},
},
wantCount: 1,
},
{
name: "change-description",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Description: "test-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
chgFn: changeDescription("test-update-description-repo"),
masks: []string{"Description"},
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Description: "test-update-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
wantCount: 1,
},
{
name: "change-name-and-description",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Description: "test-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
chgFn: combine(changeDescription("test-update-description-repo"), changeName("test-update-name-repo")),
masks: []string{"Name", "Description"},
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-update-name-repo",
Description: "test-update-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
wantCount: 1,
},
{
name: "change-username",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("pass"),
},
},
chgFn: changeUser("test-update-user"),
masks: []string{"Username"},
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "test-update-user",
Password: []byte("pass"),
},
},
wantCount: 1,
},
{
name: "change-password",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("pass"),
},
},
chgFn: changePassword("test-update-pass"),
masks: []string{"Password"},
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("test-update-pass"),
},
},
wantCount: 1,
},
{
name: "change-username-and-password",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("pass"),
},
},
chgFn: combine(changeUser("test-update-user"), changePassword("test-update-pass")),
masks: []string{"Username", "Password"},
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "test-update-user",
Password: []byte("test-update-pass"),
},
},
wantCount: 1,
},
{
name: "do-not-delete-password",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("pass"),
},
},
masks: []string{"Username"},
chgFn: combine(changeUser("test-new-user"), changePassword("")),
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "test-new-user",
Password: []byte("pass"),
},
},
wantCount: 1,
},
{
name: "do-not-delete-username",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("pass"),
},
},
masks: []string{"Password"},
chgFn: combine(changeUser(""), changePassword("test-new-password")),
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "user",
Password: []byte("test-new-password"),
},
},
wantCount: 1,
},
{
name: "delete-name",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Description: "test-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
masks: []string{"Name"},
chgFn: combine(changeDescription("test-update-description-repo"), changeName("")),
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Description: "test-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
wantCount: 1,
},
{
name: "delete-description",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Description: "test-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
masks: []string{"Description"},
chgFn: combine(changeDescription(""), changeName("test-update-name-repo")),
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Username: "user",
Password: []byte("pass"),
},
},
wantCount: 1,
},
{
name: "do-not-delete-name",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Description: "test-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
masks: []string{"Description"},
chgFn: combine(changeDescription("test-update-description-repo"), changeName("")),
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Description: "test-update-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
wantCount: 1,
},
{
name: "do-not-delete-description",
orig: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-name-repo",
Description: "test-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
masks: []string{"Name"},
chgFn: combine(changeDescription(""), changeName("test-update-name-repo")),
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Name: "test-update-name-repo",
Description: "test-description-repo",
Username: "user",
Password: []byte("pass"),
},
},
wantCount: 1,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
kkms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(ctx, rw, rw, kkms)
assert.NoError(err)
require.NotNil(repo)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
store := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
tt.orig.StoreId = store.PublicId
orig, err := repo.CreateUsernamePasswordCredential(ctx, prj.GetPublicId(), tt.orig)
assert.NoError(err)
require.NotNil(orig)
if tt.chgFn != nil {
orig = tt.chgFn(orig)
}
var version uint32
if orig != nil {
version = orig.GetVersion()
}
got, gotCount, err := repo.UpdateUsernamePasswordCredential(ctx, prj.GetPublicId(), orig, version, tt.masks)
if tt.wantErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
assert.Equal(tt.wantCount, gotCount, "row count")
assert.Nil(got)
return
}
assert.NoError(err)
assert.Empty(tt.orig.PublicId)
require.NotNil(got)
assertPublicId(t, CredentialPrefix, got.PublicId)
assert.Equal(tt.wantCount, gotCount, "row count")
assert.NotSame(tt.orig, got)
assert.Equal(tt.orig.StoreId, got.StoreId)
underlyingDB, err := conn.SqlDB(ctx)
require.NoError(err)
dbassert := dbassert.New(t, underlyingDB)
if tt.want.Name == "" {
got := got.clone()
dbassert.IsNull(got, "name")
} else {
assert.Equal(tt.want.Name, got.Name)
}
if tt.want.Description == "" {
got := got.clone()
dbassert.IsNull(got, "description")
} else {
assert.Equal(tt.want.Description, got.Description)
}
assert.Equal(tt.want.Username, got.Username)
// Validate only passwordHmac is returned
assert.Empty(got.Password)
assert.Empty(got.CtPassword)
assert.NotEmpty(got.PasswordHmac)
// Validate hmac
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase)
require.NoError(err)
hm, err := crypto.HmacSha256(ctx, tt.want.Password, databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
require.NoError(err)
assert.Equal([]byte(hm), got.PasswordHmac)
if tt.wantCount > 0 {
assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)))
}
})
}
}
@@ -0,0 +1,45 @@
package static
import (
"context"
"fmt"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
)
// Retrieve retrieves and returns static credentials from Boundary for all the provided
// ids. All the returned static credentials will have their secret fields decrypted.
func (r *Repository) Retrieve(ctx context.Context, scopeId string, ids []string) ([]credential.Static, error) {
const op = "static.(Repository).Retrieve"
if len(ids) == 0 {
return nil, errors.New(ctx, errors.InvalidParameter, op, "no ids")
}
var creds []*UsernamePasswordCredential
err := r.reader.SearchWhere(ctx, &creds, "public_id in (?)", []interface{}{ids})
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if len(creds) != len(ids) {
return nil, errors.New(ctx, errors.NotSpecificIntegrity, op,
fmt.Sprintf("mismatch between creds and number of ids requested, expected %d got %d", len(ids), len(creds)))
}
out := make([]credential.Static, 0, len(ids))
for _, c := range creds {
// decrypt credential
databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
}
if err := c.decrypt(ctx, databaseWrapper); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out = append(out, c)
}
return out, nil
}
@@ -0,0 +1,106 @@
package static
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/static/store"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepository_Retrieve(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
assert, require := assert.New(t), require.New(t)
repo, err := NewRepository(context.Background(), rw, rw, kms)
assert.NoError(err)
require.NotNil(repo)
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
staticStore := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
cred1 := TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", staticStore.GetPublicId(), prj.GetPublicId())
cred2 := TestUsernamePasswordCredential(t, conn, wrapper, "different user", "better password", staticStore.GetPublicId(), prj.GetPublicId())
cred3 := TestUsernamePasswordCredential(t, conn, wrapper, "final user", "horrible password", staticStore.GetPublicId(), prj.GetPublicId())
type args struct {
credIds []string
scopeId string
}
tests := []struct {
name string
args args
wantErr bool
wantCreds []credential.Static
}{
{
name: "no-scope",
args: args{
credIds: []string{cred1.GetPublicId()},
},
wantErr: true,
},
{
name: "invalid-scope",
args: args{
scopeId: org.GetPublicId(),
credIds: []string{cred1.GetPublicId()},
},
wantErr: true,
},
{
name: "valid-one-cred",
args: args{
scopeId: prj.GetPublicId(),
credIds: []string{cred1.GetPublicId()},
},
wantCreds: []credential.Static{
cred1,
},
},
{
name: "valid-multiple-creds",
args: args{
scopeId: prj.GetPublicId(),
credIds: []string{cred1.GetPublicId(), cred2.GetPublicId(), cred3.GetPublicId()},
},
wantCreds: []credential.Static{
cred1, cred2, cred3,
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
gotCreds, err := repo.Retrieve(context.Background(), tt.args.scopeId, tt.args.credIds)
if tt.wantErr {
require.Error(err)
assert.Nil(gotCreds)
return
}
require.NoError(err)
assert.Empty(
cmp.Diff(
tt.wantCreds,
gotCreds,
cmpopts.IgnoreUnexported(UsernamePasswordCredential{}, store.UsernamePasswordCredential{}),
cmpopts.IgnoreTypes(&timestamp.Timestamp{}),
cmpopts.SortSlices(func(x, y credential.Static) bool {
return x.GetPublicId() < y.GetPublicId()
}),
),
)
})
}
}
@@ -0,0 +1,119 @@
package static
import (
"context"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepository_New(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrapper)
type args struct {
r db.Reader
w db.Writer
kms *kms.Kms
opts []Option
}
tests := []struct {
name string
args args
want *Repository
wantIsErr errors.Code
}{
{
name: "valid",
args: args{
r: rw,
w: rw,
kms: kmsCache,
},
want: &Repository{
reader: rw,
writer: rw,
kms: kmsCache,
defaultLimit: db.DefaultLimit,
},
},
{
name: "valid-with-limit",
args: args{
r: rw,
w: rw,
kms: kmsCache,
opts: []Option{WithLimit(5)},
},
want: &Repository{
reader: rw,
writer: rw,
kms: kmsCache,
defaultLimit: 5,
},
},
{
name: "nil-reader",
args: args{
r: nil,
w: rw,
kms: kmsCache,
},
want: nil,
wantIsErr: errors.InvalidParameter,
},
{
name: "nil-writer",
args: args{
r: rw,
w: nil,
kms: kmsCache,
},
want: nil,
wantIsErr: errors.InvalidParameter,
},
{
name: "nil-kms",
args: args{
r: rw,
w: rw,
kms: nil,
},
want: nil,
wantIsErr: errors.InvalidParameter,
},
{
name: "all-nils",
args: args{
r: nil,
w: nil,
kms: nil,
},
want: nil,
wantIsErr: errors.InvalidParameter,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := NewRepository(context.Background(), tt.args.r, tt.args.w, tt.args.kms, tt.args.opts...)
if tt.wantIsErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err)
assert.Nil(got)
return
}
assert.NoError(err)
require.NotNil(got)
assert.Equal(tt.want, got)
})
}
}
@@ -0,0 +1,467 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc (unknown)
// source: controller/storage/credential/static/store/v1/static.proto
// Package store provides protobufs for storing types in the static
// credential package.
package store
import (
timestamp "github.com/hashicorp/boundary/internal/db/timestamp"
_ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type CredentialStore struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// public_id is a surrogate key suitable for use in a public API.
// @inject_tag: `gorm:"primary_key"`
PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
// The create_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
// The update_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
// name is optional. If set, it must be unique within scope_id.
// @inject_tag: `gorm:"default:null"`
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
// description is optional.
// @inject_tag: `gorm:"default:null"`
Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
// The scope_id of the owning scope.
// It must be set.
// @inject_tag: `gorm:"not_null"`
ScopeId string `protobuf:"bytes,6,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"not_null"`
// version allows optimistic locking of the resource.
// @inject_tag: `gorm:"default:null"`
Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
}
func (x *CredentialStore) Reset() {
*x = CredentialStore{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CredentialStore) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CredentialStore) ProtoMessage() {}
func (x *CredentialStore) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CredentialStore.ProtoReflect.Descriptor instead.
func (*CredentialStore) Descriptor() ([]byte, []int) {
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{0}
}
func (x *CredentialStore) GetPublicId() string {
if x != nil {
return x.PublicId
}
return ""
}
func (x *CredentialStore) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *CredentialStore) GetUpdateTime() *timestamp.Timestamp {
if x != nil {
return x.UpdateTime
}
return nil
}
func (x *CredentialStore) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *CredentialStore) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *CredentialStore) GetScopeId() string {
if x != nil {
return x.ScopeId
}
return ""
}
func (x *CredentialStore) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
type UsernamePasswordCredential struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// public_id is a surrogate key suitable for use in a public API.
// @inject_tag: `gorm:"primary_key"`
PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
// create_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
// update_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
// name is optional. If set, it must be unique within scope_id.
// @inject_tag: `gorm:"default:null"`
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
// description is optional.
// @inject_tag: `gorm:"default:null"`
Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
// store_id of the owning static credential store.
// It must be set.
// @inject_tag: `gorm:"not_null"`
StoreId string `protobuf:"bytes,6,opt,name=store_id,json=storeId,proto3" json:"store_id,omitempty" gorm:"not_null"`
// version allows optimistic locking of the resource.
// @inject_tag: `gorm:"default:null"`
Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
// username is the username associated with the credential.
// It must be set.
// @inject_tag: `gorm:"not_null"`
Username string `protobuf:"bytes,8,opt,name=username,proto3" json:"username,omitempty" gorm:"not_null"`
// password is the plain-text of the password associated with the credential. We are
// not storing this plain-text password in the database.
// @inject_tag: `gorm:"-" wrapping:"pt,password_data"`
Password []byte `protobuf:"bytes,9,opt,name=password,proto3" json:"password,omitempty" gorm:"-" wrapping:"pt,password_data"`
// ct_password is the ciphertext of the password. It
// is stored in the database.
// @inject_tag: `gorm:"column:password_encrypted;not_null" wrapping:"ct,password_data"`
CtPassword []byte `protobuf:"bytes,10,opt,name=ct_password,json=ctPassword,proto3" json:"ct_password,omitempty" gorm:"column:password_encrypted;not_null" wrapping:"ct,password_data"`
// password_hmac is a sha256-hmac of the unencrypted password. It is recalculated
// everytime the password is updated.
// @inject_tag: `gorm:"not_null"`
PasswordHmac []byte `protobuf:"bytes,11,opt,name=password_hmac,json=passwordHmac,proto3" json:"password_hmac,omitempty" gorm:"not_null"`
// The key_id of the kms database key used for encrypting this entry.
// It must be set.
// @inject_tag: `gorm:"not_null"`
KeyId string `protobuf:"bytes,12,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"`
}
func (x *UsernamePasswordCredential) Reset() {
*x = UsernamePasswordCredential{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *UsernamePasswordCredential) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UsernamePasswordCredential) ProtoMessage() {}
func (x *UsernamePasswordCredential) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UsernamePasswordCredential.ProtoReflect.Descriptor instead.
func (*UsernamePasswordCredential) Descriptor() ([]byte, []int) {
return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{1}
}
func (x *UsernamePasswordCredential) GetPublicId() string {
if x != nil {
return x.PublicId
}
return ""
}
func (x *UsernamePasswordCredential) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *UsernamePasswordCredential) GetUpdateTime() *timestamp.Timestamp {
if x != nil {
return x.UpdateTime
}
return nil
}
func (x *UsernamePasswordCredential) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *UsernamePasswordCredential) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *UsernamePasswordCredential) GetStoreId() string {
if x != nil {
return x.StoreId
}
return ""
}
func (x *UsernamePasswordCredential) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
func (x *UsernamePasswordCredential) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *UsernamePasswordCredential) GetPassword() []byte {
if x != nil {
return x.Password
}
return nil
}
func (x *UsernamePasswordCredential) GetCtPassword() []byte {
if x != nil {
return x.CtPassword
}
return nil
}
func (x *UsernamePasswordCredential) GetPasswordHmac() []byte {
if x != nil {
return x.PasswordHmac
}
return nil
}
func (x *UsernamePasswordCredential) GetKeyId() string {
if x != nil {
return x.KeyId
}
return ""
}
var File_controller_storage_credential_static_store_v1_static_proto protoreflect.FileDescriptor
var file_controller_storage_credential_static_store_v1_static_proto_rawDesc = []byte{
0x0a, 0x3a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f,
0x72, 0x61, 0x67, 0x65, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f,
0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f,
0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x2d, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x74,
0x69, 0x63, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2a, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe5, 0x02, 0x0a, 0x0f, 0x43, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x1b, 0x0a, 0x09,
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65,
0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a,
0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72,
0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31,
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54,
0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73,
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e,
0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b,
0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x73,
0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73,
0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x22, 0xfd, 0x04, 0x0a, 0x1a, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73,
0x73, 0x77, 0x6f, 0x72, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12,
0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73,
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a,
0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72,
0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31,
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61,
0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65,
0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b,
0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28,
0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19,
0x0a, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x23, 0xc2, 0xdd, 0x29, 0x1f, 0x0a, 0x08, 0x55, 0x73, 0x65,
0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x23, 0xc2, 0xdd, 0x29, 0x1f, 0x0a, 0x08, 0x50, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
0x65, 0x73, 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x70, 0x61, 0x73,
0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x74, 0x5f, 0x70, 0x61, 0x73, 0x73,
0x77, 0x6f, 0x72, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x74, 0x50, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x51, 0x0a, 0x0d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x2c, 0xc2,
0xdd, 0x29, 0x28, 0x0a, 0x0c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x6d, 0x61,
0x63, 0x12, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x52, 0x0c, 0x70, 0x61, 0x73,
0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x6d, 0x61, 0x63, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79,
0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64,
0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68,
0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72,
0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x2f, 0x73, 0x74, 0x6f,
0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_controller_storage_credential_static_store_v1_static_proto_rawDescOnce sync.Once
file_controller_storage_credential_static_store_v1_static_proto_rawDescData = file_controller_storage_credential_static_store_v1_static_proto_rawDesc
)
func file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP() []byte {
file_controller_storage_credential_static_store_v1_static_proto_rawDescOnce.Do(func() {
file_controller_storage_credential_static_store_v1_static_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_credential_static_store_v1_static_proto_rawDescData)
})
return file_controller_storage_credential_static_store_v1_static_proto_rawDescData
}
var file_controller_storage_credential_static_store_v1_static_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_controller_storage_credential_static_store_v1_static_proto_goTypes = []interface{}{
(*CredentialStore)(nil), // 0: controller.storage.credential.static.store.v1.CredentialStore
(*UsernamePasswordCredential)(nil), // 1: controller.storage.credential.static.store.v1.UsernamePasswordCredential
(*timestamp.Timestamp)(nil), // 2: controller.storage.timestamp.v1.Timestamp
}
var file_controller_storage_credential_static_store_v1_static_proto_depIdxs = []int32{
2, // 0: controller.storage.credential.static.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
2, // 1: controller.storage.credential.static.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
2, // 2: controller.storage.credential.static.store.v1.UsernamePasswordCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
2, // 3: controller.storage.credential.static.store.v1.UsernamePasswordCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_controller_storage_credential_static_store_v1_static_proto_init() }
func file_controller_storage_credential_static_store_v1_static_proto_init() {
if File_controller_storage_credential_static_store_v1_static_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_controller_storage_credential_static_store_v1_static_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CredentialStore); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_storage_credential_static_store_v1_static_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UsernamePasswordCredential); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_storage_credential_static_store_v1_static_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_controller_storage_credential_static_store_v1_static_proto_goTypes,
DependencyIndexes: file_controller_storage_credential_static_store_v1_static_proto_depIdxs,
MessageInfos: file_controller_storage_credential_static_store_v1_static_proto_msgTypes,
}.Build()
File_controller_storage_credential_static_store_v1_static_proto = out.File
file_controller_storage_credential_static_store_v1_static_proto_rawDesc = nil
file_controller_storage_credential_static_store_v1_static_proto_goTypes = nil
file_controller_storage_credential_static_store_v1_static_proto_depIdxs = nil
}
+131
View File
@@ -0,0 +1,131 @@
package static
import (
"context"
"testing"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/kms"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestCredentialStore creates a static credential store in the provided DB with
// the provided scope and any values passed in through the Options vars.
// If any errors are encountered during the creation of the store, the test will fail.
func TestCredentialStore(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, scopeId string, opts ...Option) *CredentialStore {
t.Helper()
ctx := context.Background()
kmsCache := kms.TestKms(t, conn, wrapper)
w := db.New(conn)
databaseWrapper, err := kmsCache.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
assert.NoError(t, err)
require.NotNil(t, databaseWrapper)
cs, err := NewCredentialStore(scopeId, opts...)
assert.NoError(t, err)
require.NotNil(t, cs)
id, err := newCredentialStoreId(ctx)
assert.NoError(t, err)
require.NotEmpty(t, id)
cs.PublicId = id
_, err2 := w.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
func(_ db.Reader, iw db.Writer) error {
require.NoError(t, iw.Create(ctx, cs))
return nil
},
)
require.NoError(t, err2)
return cs
}
// TestCredentialStores creates count number of static credential stores in
// the provided DB with the provided scope id. If any errors are
// encountered during the creation of the credential stores, the test will
// fail.
func TestCredentialStores(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, scopeId string, count int) []*CredentialStore {
t.Helper()
ctx := context.Background()
kmsCache := kms.TestKms(t, conn, wrapper)
databaseWrapper, err := kmsCache.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
assert.NoError(t, err)
require.NotNil(t, databaseWrapper)
css := make([]*CredentialStore, 0, count)
for i := 0; i < count; i++ {
css = append(css, TestCredentialStore(t, conn, wrapper, scopeId))
}
return css
}
// TestUsernamePasswordCredential creates a user password credential in the provided DB with
// the provided scope and any values passed in through.
// If any errors are encountered during the creation of the store, the test will fail.
func TestUsernamePasswordCredential(
t testing.TB,
conn *db.DB,
wrapper wrapping.Wrapper,
username, password, storeId, scopeId string,
opts ...Option,
) *UsernamePasswordCredential {
t.Helper()
ctx := context.Background()
kmsCache := kms.TestKms(t, conn, wrapper)
w := db.New(conn)
databaseWrapper, err := kmsCache.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
assert.NoError(t, err)
require.NotNil(t, databaseWrapper)
cred, err := NewUsernamePasswordCredential(storeId, username, credential.Password(password), opts...)
require.NoError(t, err)
require.NotNil(t, cred)
id, err := newCredentialId(ctx)
require.NoError(t, err)
cred.PublicId = id
err = cred.encrypt(ctx, databaseWrapper)
require.NoError(t, err)
_, err2 := w.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
func(_ db.Reader, iw db.Writer) error {
require.NoError(t, iw.Create(ctx, cred))
return nil
},
)
require.NoError(t, err2)
return cred
}
// TestUsernamePasswordCredentials creates count number of user password credentials in
// the provided DB with the provided scope id. If any errors are
// encountered during the creation of the credentials, the test will fail.
func TestUsernamePasswordCredentials(
t testing.TB,
conn *db.DB,
wrapper wrapping.Wrapper,
username, password, storeId, scopeId string,
count int,
) []*UsernamePasswordCredential {
t.Helper()
ctx := context.Background()
kmsCache := kms.TestKms(t, conn, wrapper)
databaseWrapper, err := kmsCache.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
assert.NoError(t, err)
require.NotNil(t, databaseWrapper)
creds := make([]*UsernamePasswordCredential, 0, count)
for i := 0; i < count; i++ {
creds = append(creds, TestUsernamePasswordCredential(t, conn, wrapper, username, password, storeId, scopeId))
}
return creds
}
@@ -0,0 +1,86 @@
package static
import (
"context"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_TestCredentialStore(t *testing.T) {
t.Parallel()
assert, require := assert.New(t), require.New(t)
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NotNil(prj)
assert.NotEmpty(prj.GetPublicId())
cs := TestCredentialStore(t, conn, wrapper, prj.GetPublicId(), WithName("my-name"), WithDescription("my-description"))
require.NotNil(cs)
assert.NotEmpty(cs.GetPublicId())
assert.Equal(cs.Name, "my-name")
assert.Equal(cs.Description, "my-description")
}
func Test_TestCredentialStores(t *testing.T) {
t.Parallel()
assert, require := assert.New(t), require.New(t)
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NotNil(prj)
assert.NotEmpty(prj.GetPublicId())
count := 4
css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), count)
assert.Len(css, count)
}
func Test_TestUsernamePasswordCredential(t *testing.T) {
t.Parallel()
assert, require := assert.New(t), require.New(t)
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kkms := kms.TestKms(t, conn, wrapper)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NotNil(prj)
assert.NotEmpty(prj.GetPublicId())
store := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
cred := TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId(), WithName("my-name"), WithDescription("my-description"))
require.NotNil(cred)
assert.NotEmpty(cred.GetPublicId())
assert.Equal(cred.Name, "my-name")
assert.Equal(cred.Description, "my-description")
assert.Equal(cred.Username, "user")
assert.Equal(cred.Password, []byte("pass"))
// Validate hmac
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.PublicId, kms.KeyPurposeDatabase)
hm, err := crypto.HmacSha256(context.Background(), cred.Password, databaseWrapper, []byte(cred.StoreId), nil, crypto.WithEd25519())
require.NoError(err)
assert.Equal([]byte(hm), cred.PasswordHmac)
}
func Test_TestUsernamePasswordCredentials(t *testing.T) {
t.Parallel()
assert, require := assert.New(t), require.New(t)
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NotNil(prj)
assert.NotEmpty(prj.GetPublicId())
store := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
count := 4
creds := TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId(), count)
assert.Len(creds, count)
}
@@ -0,0 +1,123 @@
package static
import (
"context"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/static/store"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/libs/crypto"
"github.com/hashicorp/boundary/internal/oplog"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/hashicorp/go-kms-wrapping/v2/extras/structwrapping"
"google.golang.org/protobuf/proto"
)
var _ credential.Static = (*UsernamePasswordCredential)(nil)
// A UsernamePasswordCredential contains the credential with a username and password.
// It is owned by a credential store.
type UsernamePasswordCredential struct {
*store.UsernamePasswordCredential
tableName string `gorm:"-"`
}
// NewUsernamePasswordCredential creates a new in memory static Credential containing a
// username and password that is assigned to storeId. Name and description are the only
// valid options. All other options are ignored.
func NewUsernamePasswordCredential(
storeId string,
username string,
password credential.Password,
opt ...Option,
) (*UsernamePasswordCredential, error) {
opts := getOpts(opt...)
l := &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
StoreId: storeId,
Name: opts.withName,
Description: opts.withDescription,
Username: username,
Password: []byte(password),
},
}
return l, nil
}
func allocUsernamePasswordCredential() *UsernamePasswordCredential {
return &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{},
}
}
func (c *UsernamePasswordCredential) clone() *UsernamePasswordCredential {
cp := proto.Clone(c.UsernamePasswordCredential)
return &UsernamePasswordCredential{
UsernamePasswordCredential: cp.(*store.UsernamePasswordCredential),
}
}
// TableName returns the table name.
func (c *UsernamePasswordCredential) TableName() string {
if c.tableName != "" {
return c.tableName
}
return "credential_static_username_password_credential"
}
// SetTableName sets the table name.
func (c *UsernamePasswordCredential) SetTableName(n string) {
c.tableName = n
}
func (c *UsernamePasswordCredential) oplog(op oplog.OpType) oplog.Metadata {
metadata := oplog.Metadata{
"resource-public-id": []string{c.PublicId},
"resource-type": []string{"credential-static-username-password"},
"op-type": []string{op.String()},
}
if c.StoreId != "" {
metadata["store-id"] = []string{c.StoreId}
}
return metadata
}
func (c *UsernamePasswordCredential) encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
const op = "static.(UsernamePasswordCredential).encrypt"
if len(c.Password) == 0 {
return errors.New(ctx, errors.InvalidParameter, op, "no password defined")
}
if err := structwrapping.WrapStruct(ctx, cipher, c.UsernamePasswordCredential, nil); err != nil {
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt))
}
keyId, err := cipher.KeyId(ctx)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("error reading cipher key id"))
}
c.KeyId = keyId
if err := c.hmacPassword(ctx, cipher); err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
}
func (c *UsernamePasswordCredential) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
const op = "static.(UsernamePasswordCredential).decrypt"
if err := structwrapping.UnwrapStruct(ctx, cipher, c.UsernamePasswordCredential, nil); err != nil {
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt))
}
return nil
}
func (c *UsernamePasswordCredential) hmacPassword(ctx context.Context, cipher wrapping.Wrapper) error {
const op = "static.(UsernamePasswordCredential).hmacPassword"
if cipher == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing cipher")
}
hm, err := crypto.HmacSha256(ctx, c.Password, cipher, []byte(c.StoreId), nil, crypto.WithEd25519())
if err != nil {
return errors.Wrap(ctx, err, op)
}
c.PasswordHmac = []byte(hm)
return nil
}
@@ -0,0 +1,178 @@
package static
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/static/store"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/testing/protocmp"
)
func TestCredential_New(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kkms := kms.TestKms(t, conn, wrapper)
rw := db.New(conn)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cs := TestCredentialStore(t, conn, wrapper, prj.PublicId)
type args struct {
username string
password credential.Password
storeId string
options []Option
}
tests := []struct {
name string
args args
want *UsernamePasswordCredential
wantCreateErr bool
wantEncryptErr bool
}{
{
name: "missing-password",
args: args{
username: "test-user",
storeId: cs.PublicId,
},
want: allocUsernamePasswordCredential(),
wantEncryptErr: true,
},
{
name: "missing-username",
args: args{
password: "test-pass",
storeId: cs.PublicId,
},
want: allocUsernamePasswordCredential(),
wantCreateErr: true,
},
{
name: "missing-store-id",
args: args{
username: "test-user",
password: "test-pass",
},
want: allocUsernamePasswordCredential(),
wantCreateErr: true,
},
{
name: "valid-no-options",
args: args{
username: "test-user",
password: "test-pass",
storeId: cs.PublicId,
},
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "test-user",
Password: []byte("test-pass"),
StoreId: cs.PublicId,
},
},
},
{
name: "valid-with-name",
args: args{
username: "test-user",
password: "test-pass",
storeId: cs.PublicId,
options: []Option{WithName("my-credential")},
},
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "test-user",
Password: []byte("test-pass"),
StoreId: cs.PublicId,
Name: "my-credential",
},
},
},
{
name: "valid-with-description",
args: args{
username: "test-user",
password: "test-pass",
storeId: cs.PublicId,
options: []Option{WithDescription("my-credential-description")},
},
want: &UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
Username: "test-user",
Password: []byte("test-pass"),
StoreId: cs.PublicId,
Description: "my-credential-description",
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
got, err := NewUsernamePasswordCredential(tt.args.storeId, tt.args.username, tt.args.password, tt.args.options...)
require.NoError(err)
require.NotNil(got)
assert.Emptyf(got.PublicId, "PublicId set")
id, err := newCredentialId(ctx)
require.NoError(err)
tt.want.PublicId = id
got.PublicId = id
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.PublicId, kms.KeyPurposeDatabase)
err = got.encrypt(ctx, databaseWrapper)
if tt.wantEncryptErr {
require.Error(err)
return
}
assert.NoError(err)
err = rw.Create(context.Background(), got)
if tt.wantCreateErr {
require.Error(err)
return
}
assert.NoError(err)
got2 := allocUsernamePasswordCredential()
got2.PublicId = id
assert.Equal(id, got2.GetPublicId())
require.NoError(rw.LookupById(ctx, got2))
err = got2.decrypt(ctx, databaseWrapper)
require.NoError(err)
// Timestamps and version are automatically set
tt.want.CreateTime = got2.CreateTime
tt.want.UpdateTime = got2.UpdateTime
tt.want.Version = got2.Version
// KeyId is allocated via kms no need to validate in this test
tt.want.KeyId = got2.KeyId
got2.CtPassword = nil
// encrypt also calculates the hmac, validate it is correct
hm, err := crypto.HmacSha256(ctx, got.Password, databaseWrapper, []byte(got.StoreId), nil, crypto.WithEd25519())
require.NoError(err)
tt.want.PasswordHmac = []byte(hm)
assert.Empty(cmp.Diff(tt.want, got2.clone(), protocmp.Transform()))
})
}
}
@@ -214,7 +214,7 @@ func TestCredentialLibrary_New(t *testing.T) {
vaultPath: "vault/path",
opts: []Option{
WithMethod(MethodGet),
WithCredentialType(credential.UserPasswordType),
WithCredentialType(credential.UsernamePasswordType),
},
},
want: &CredentialLibrary{
@@ -222,7 +222,7 @@ func TestCredentialLibrary_New(t *testing.T) {
StoreId: cs.PublicId,
VaultPath: "vault/path",
HttpMethod: string(MethodGet),
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
},
@@ -233,7 +233,7 @@ func TestCredentialLibrary_New(t *testing.T) {
vaultPath: "vault/path",
opts: []Option{
WithMethod(MethodGet),
WithCredentialType(credential.UserPasswordType),
WithCredentialType(credential.UsernamePasswordType),
WithMappingOverride(NewUserPasswordOverride(WithOverrideUsernameAttribute("test"))),
},
},
@@ -243,7 +243,7 @@ func TestCredentialLibrary_New(t *testing.T) {
StoreId: cs.PublicId,
VaultPath: "vault/path",
HttpMethod: string(MethodGet),
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
},
@@ -262,8 +262,8 @@ func TestCredentialLibrary_New(t *testing.T) {
assert.Equal(tt.want, got)
switch ct := tt.want.GetCredentialType(); ct {
case string(credential.UserPasswordType):
assert.Equal(credential.UserPasswordType, got.CredentialType())
case string(credential.UsernamePasswordType):
assert.Equal(credential.UsernamePasswordType, got.CredentialType())
case string(credential.UnspecifiedType), "":
assert.Equal(credential.UnspecifiedType, got.CredentialType())
default:
@@ -15,7 +15,7 @@ func validMappingOverride(m MappingOverride, ct credential.Type) bool {
case nil:
return true // it is always valid to not specify a mapping override
case *UserPasswordOverride:
return ct == credential.UserPasswordType
return ct == credential.UsernamePasswordType
default:
return false // an unknown mapping override type is never valid
}
@@ -34,7 +34,7 @@ func TestValidMappingOverrides(t *testing.T) {
},
{
m: nil,
ct: credential.UserPasswordType,
ct: credential.UsernamePasswordType,
want: true,
},
{
@@ -44,7 +44,7 @@ func TestValidMappingOverrides(t *testing.T) {
},
{
m: unknownMapper(1),
ct: credential.UserPasswordType,
ct: credential.UsernamePasswordType,
want: false,
},
{
@@ -54,7 +54,7 @@ func TestValidMappingOverrides(t *testing.T) {
},
{
m: allocUserPasswordOverride(),
ct: credential.UserPasswordType,
ct: credential.UsernamePasswordType,
want: true,
},
}
+2 -2
View File
@@ -85,9 +85,9 @@ func Test_GetOpts(t *testing.T) {
assert.Equal(t, opts, testOpts)
})
t.Run("WithCredentialType", func(t *testing.T) {
opts := getOpts(WithCredentialType(credential.UserPasswordType))
opts := getOpts(WithCredentialType(credential.UsernamePasswordType))
testOpts := getDefaultOptions()
testOpts.withCredentialType = credential.UserPasswordType
testOpts.withCredentialType = credential.UsernamePasswordType
assert.Equal(t, opts, testOpts)
})
t.Run("WithOverrideUsernameAttribute", func(t *testing.T) {
+3 -3
View File
@@ -18,7 +18,7 @@ import (
"google.golang.org/protobuf/proto"
)
var _ credential.UserPassword = (*usrPassCred)(nil)
var _ credential.UsernamePassword = (*usrPassCred)(nil)
type usrPassCred struct {
*baseCred
@@ -47,7 +47,7 @@ func (bc *baseCred) getExpiration() time.Duration { return bc.expiration }
// UnspecifiedType.
func convert(ctx context.Context, bc *baseCred) (dynamicCred, error) {
switch bc.Library().CredentialType() {
case credential.UserPasswordType:
case credential.UsernamePasswordType:
return baseToUsrPass(ctx, bc)
}
return bc, nil
@@ -59,7 +59,7 @@ func baseToUsrPass(ctx context.Context, bc *baseCred) (*usrPassCred, error) {
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("nil baseCred"))
case bc.lib == nil:
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("nil baseCred.lib"))
case bc.Library().CredentialType() != credential.UserPasswordType:
case bc.Library().CredentialType() != credential.UsernamePasswordType:
return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("invalid credential type"))
}
@@ -115,7 +115,7 @@ func TestRepository_getPrivateLibraries(t *testing.T) {
}
{
opts := []Option{
WithCredentialType(credential.UserPasswordType),
WithCredentialType(credential.UsernamePasswordType),
}
libIn, err := NewCredentialLibrary(origStore.GetPublicId(), "/vault/path", opts...)
assert.NoError(err)
@@ -129,7 +129,7 @@ func TestRepository_getPrivateLibraries(t *testing.T) {
}
{
opts := []Option{
WithCredentialType(credential.UserPasswordType),
WithCredentialType(credential.UsernamePasswordType),
WithMappingOverride(NewUserPasswordOverride(
WithOverrideUsernameAttribute("test-username"),
)),
@@ -146,7 +146,7 @@ func TestRepository_getPrivateLibraries(t *testing.T) {
}
{
opts := []Option{
WithCredentialType(credential.UserPasswordType),
WithCredentialType(credential.UsernamePasswordType),
WithMappingOverride(NewUserPasswordOverride(
WithOverridePasswordAttribute("test-password"),
)),
@@ -163,7 +163,7 @@ func TestRepository_getPrivateLibraries(t *testing.T) {
}
{
opts := []Option{
WithCredentialType(credential.UserPasswordType),
WithCredentialType(credential.UsernamePasswordType),
WithMappingOverride(NewUserPasswordOverride(
WithOverrideUsernameAttribute("test-username"),
WithOverridePasswordAttribute("test-password"),
@@ -336,7 +336,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-no-username-default-password-attribute",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"password": "my-password",
@@ -348,7 +348,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-no-password-default-username-attribute",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"username": "my-username",
@@ -360,7 +360,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "valid-default-attributes",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"username": "my-username",
@@ -376,7 +376,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "valid-override-attributes",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
UsernameAttribute: "test-username",
PasswordAttribute: "test-password",
},
@@ -396,7 +396,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "valid-default-username-override-password",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
PasswordAttribute: "test-password",
},
secretData: map[string]interface{}{
@@ -415,7 +415,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "valid-override-username-default-password",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
UsernameAttribute: "test-username",
},
secretData: map[string]interface{}{
@@ -434,7 +434,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-username-override",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
UsernameAttribute: "missing-username",
},
secretData: map[string]interface{}{
@@ -450,7 +450,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-password-override",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
UsernameAttribute: "missing-password",
},
secretData: map[string]interface{}{
@@ -466,7 +466,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-kv2-no-metadata-field",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"data": map[string]interface{}{
@@ -481,7 +481,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-kv2-no-data-field",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
@@ -493,7 +493,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-kv2-no-username-default-password-attribute",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
@@ -508,7 +508,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-kv2-no-passsword-default-username-attribute",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
@@ -523,7 +523,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-kv2-invalid-metadata-type",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"metadata": "hello",
@@ -539,7 +539,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-kv2-invalid-metadata-type",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
@@ -552,7 +552,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "invalid-kv2-additional-field",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"bad-field": "hello",
@@ -569,7 +569,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "valid-kv2-default-attributes",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
},
secretData: map[string]interface{}{
"metadata": map[string]interface{}{},
@@ -588,7 +588,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "valid-kv2-override-attributes",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
UsernameAttribute: "test-username",
PasswordAttribute: "test-password",
},
@@ -611,7 +611,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "valid-kv2-default-username-override-password",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
PasswordAttribute: "test-password",
},
secretData: map[string]interface{}{
@@ -633,7 +633,7 @@ func TestBaseToUsrPass(t *testing.T) {
name: "valid-kv2-override-username-default-password",
given: &baseCred{
lib: &privateLibrary{
CredType: string(credential.UserPasswordType),
CredType: string(credential.UsernamePasswordType),
UsernameAttribute: "test-username",
},
secretData: map[string]interface{}{
@@ -171,7 +171,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
want: &CredentialLibrary{
@@ -179,7 +179,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
},
@@ -191,7 +191,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
wantErr: errors.VaultInvalidMappingOverride,
@@ -218,7 +218,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
want: &CredentialLibrary{
@@ -229,7 +229,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
},
@@ -243,7 +243,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
want: &CredentialLibrary{
@@ -254,7 +254,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
},
@@ -269,7 +269,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
want: &CredentialLibrary{
@@ -281,7 +281,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
},
@@ -761,7 +761,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/old/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
chgFn: changeVaultPath("/new/path"),
@@ -771,7 +771,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
CredentialLibrary: &store.CredentialLibrary{
HttpMethod: "GET",
VaultPath: "/new/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
wantCount: 1,
@@ -921,7 +921,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
chgFn: changeCredentialType(credential.UnspecifiedType),
@@ -939,7 +939,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
chgFn: changeMappingOverride(
@@ -956,7 +956,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
wantCount: 1,
@@ -972,7 +972,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
chgFn: changeMappingOverride(
@@ -989,7 +989,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
wantCount: 1,
@@ -1005,7 +1005,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
chgFn: changeMappingOverride(
@@ -1024,7 +1024,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
wantCount: 1,
@@ -1036,7 +1036,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
chgFn: changeMappingOverride(
@@ -1055,7 +1055,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
wantCount: 1,
@@ -1071,7 +1071,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
chgFn: changeMappingOverride(nil),
@@ -1081,7 +1081,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
wantCount: 1,
@@ -1324,7 +1324,7 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
},
@@ -1338,7 +1338,7 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
},
@@ -1352,7 +1352,7 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
},
@@ -1367,7 +1367,7 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) {
StoreId: cs.GetPublicId(),
HttpMethod: "GET",
VaultPath: "/some/path",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
},
},
@@ -1528,7 +1528,7 @@ func TestRepository_DeleteCredentialLibrary(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
}
@@ -1572,7 +1572,7 @@ func TestRepository_ListCredentialLibraries(t *testing.T) {
HttpMethod: "GET",
VaultPath: "/some/path",
Name: "test-name-repo",
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
},
}
@@ -128,7 +128,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
{
libPath := path.Join("database", "creds", "opened")
opts := []vault.Option{
vault.WithCredentialType(credential.UserPasswordType),
vault.WithCredentialType(credential.UsernamePasswordType),
}
libIn, err := vault.NewCredentialLibrary(origStore.GetPublicId(), libPath, opts...)
assert.NoError(t, err)
@@ -141,7 +141,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
{
libPath := path.Join("database", "creds", "opened")
opts := []vault.Option{
vault.WithCredentialType(credential.UserPasswordType),
vault.WithCredentialType(credential.UsernamePasswordType),
vault.WithMappingOverride(vault.NewUserPasswordOverride(
vault.WithOverrideUsernameAttribute("test-username"),
vault.WithOverridePasswordAttribute("test-password"),
@@ -158,7 +158,7 @@ func TestRepository_IssueCredentials(t *testing.T) {
{
libPath := path.Join("secret", "data", "my-secret")
opts := []vault.Option{
vault.WithCredentialType(credential.UserPasswordType),
vault.WithCredentialType(credential.UsernamePasswordType),
}
libIn, err := vault.NewCredentialLibrary(origStore.GetPublicId(), libPath, opts...)
assert.NoError(t, err)
@@ -326,15 +326,15 @@ func TestRepository_IssueCredentials(t *testing.T) {
assert.NotZero(len(got))
for _, dc := range got {
switch dc.Library().CredentialType() {
case credential.UserPasswordType:
if upc, ok := dc.(credential.UserPassword); ok {
case credential.UsernamePasswordType:
if upc, ok := dc.(credential.UsernamePassword); ok {
assert.NotEmpty(upc.Username())
assert.NotEmpty(upc.Password())
break
}
assert.Fail("want UserPassword credential from library with credential type UserPassword")
assert.Fail("want UserPassword credential from library with credential type UsernamePassword")
case credential.UnspecifiedType:
if _, ok := dc.(credential.UserPassword); ok {
if _, ok := dc.(credential.UsernamePassword); ok {
assert.Fail("do not want UserPassword credential from library with credential type Unspecified")
}
}
@@ -79,7 +79,7 @@ func TestSetupSleepyDevEnvironment(t *testing.T) {
)
require.NoError(t, err)
require.NotNil(t, lib)
tgt, err = tcl.AddCredentialLibraries(ctx, tgt.Item.Id, 0, targets.WithAutomaticVersioning(true), targets.WithApplicationCredentialLibraryIds([]string{lib.Item.Id}))
tgt, err = tcl.AddCredentialSources(ctx, tgt.Item.Id, 0, targets.WithAutomaticVersioning(true), targets.WithApplicationCredentialSourceIds([]string{lib.Item.Id}))
require.NoError(t, err)
require.NotNil(t, tgt)
// Worker 1
@@ -3,6 +3,7 @@ package common
import (
"github.com/hashicorp/boundary/internal/auth/oidc"
"github.com/hashicorp/boundary/internal/auth/password"
credstatic "github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/credential/vault"
pluginhost "github.com/hashicorp/boundary/internal/host/plugin"
"github.com/hashicorp/boundary/internal/host/static"
@@ -16,6 +17,7 @@ import (
type (
AuthTokenRepoFactory = oidc.AuthTokenRepoFactory
VaultCredentialRepoFactory = func() (*vault.Repository, error)
StaticCredentialRepoFactory = func() (*credstatic.Repository, error)
IamRepoFactory func() (*iam.Repository, error)
OidcAuthRepoFactory = oidc.OidcRepoFactory
PasswordAuthRepoFactory func() (*password.Repository, error)
+7 -3
View File
@@ -12,6 +12,7 @@ import (
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/config"
credstatic "github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/credential/vault"
"github.com/hashicorp/boundary/internal/daemon/controller/common"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/health"
@@ -67,6 +68,7 @@ type Controller struct {
// Repo factory methods
AuthTokenRepoFn common.AuthTokenRepoFactory
VaultCredentialRepoFn common.VaultCredentialRepoFactory
StaticCredentialRepoFn common.StaticCredentialRepoFactory
IamRepoFn common.IamRepoFactory
OidcRepoFn common.OidcAuthRepoFactory
PasswordAuthRepoFn common.PasswordAuthRepoFactory
@@ -249,9 +251,8 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
jobRepoFn := func() (*job.Repository, error) {
return job.NewRepository(dbase, dbase, c.kms)
}
// TODO: the RunJobsLimit is temporary until a better fix gets in. This
// currently caps the scheduler at running 10 jobs per interval.
schedulerOpts := []scheduler.Option{scheduler.WithRunJobsLimit(20)}
// TODO: Allow setting run jobs limit from config
schedulerOpts := []scheduler.Option{scheduler.WithRunJobsLimit(-1)}
if c.conf.RawConfig.Controller.SchedulerRunJobInterval > 0 {
schedulerOpts = append(schedulerOpts, scheduler.WithRunJobsInterval(c.conf.RawConfig.Controller.SchedulerRunJobInterval))
}
@@ -279,6 +280,9 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
c.VaultCredentialRepoFn = func() (*vault.Repository, error) {
return vault.NewRepository(dbase, dbase, c.kms, c.scheduler)
}
c.StaticCredentialRepoFn = func() (*credstatic.Repository, error) {
return credstatic.NewRepository(ctx, dbase, dbase, c.kms)
}
c.ServersRepoFn = func() (*servers.Repository, error) {
return servers.NewRepository(dbase, dbase, c.kms)
}
@@ -2,12 +2,17 @@ package controller
import (
"context"
"os"
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/config"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/types/scope"
host_plugin_assets "github.com/hashicorp/boundary/plugins/host"
"github.com/hashicorp/go-secure-stdlib/listenerutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -223,3 +228,39 @@ func TestControllerNewListenerConfig(t *testing.T) {
})
}
}
func TestController_NewPluginsConfig(t *testing.T) {
require := require.New(t)
testCtx := context.Background()
ctx, cancel := context.WithCancel(context.Background())
tc := &TestController{
t: t,
ctx: ctx,
cancel: cancel,
opts: nil,
}
initialConfig, err := config.DevController()
require.NoError(err)
tmpDir := t.TempDir()
initialConfig.Plugins.ExecutionDir = tmpDir
conf := TestControllerConfig(t, ctx, tc, &TestControllerOpts{Config: initialConfig})
conf.EnabledPlugins = []base.EnabledPlugin{
base.EnabledPluginHostAws,
base.EnabledPluginHostAzure,
}
_, err = New(testCtx, conf)
require.NoError(err)
// Check that both plugins were written to the temp dir
files, err := os.ReadDir(tmpDir)
require.NoError(err)
require.Len(files, 2)
var pluginNames []string
for _, file := range files {
pluginNames = append(pluginNames, filepath.Base(file.Name()))
}
expectedPluginNames := []string{host_plugin_assets.HostPluginPrefix + "aws.gz", host_plugin_assets.HostPluginPrefix + "azure.gz"}
require.Empty(cmp.Diff(expectedPluginNames, pluginNames))
}
+14 -2
View File
@@ -22,6 +22,7 @@ import (
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/authmethods"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/authtokens"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentials"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentialstores"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/groups"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/health"
@@ -175,7 +176,8 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
c.SessionRepoFn,
c.PluginHostRepoFn,
c.StaticHostRepoFn,
c.VaultCredentialRepoFn)
c.VaultCredentialRepoFn,
c.StaticCredentialRepoFn)
if err != nil {
return fmt.Errorf("failed to create target handler service: %w", err)
}
@@ -210,7 +212,7 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
services.RegisterManagedGroupServiceServer(s, mgs)
}
if _, ok := currentServices[services.CredentialStoreService_ServiceDesc.ServiceName]; !ok {
cs, err := credentialstores.NewService(c.VaultCredentialRepoFn, c.IamRepoFn)
cs, err := credentialstores.NewService(c.VaultCredentialRepoFn, c.StaticCredentialRepoFn, c.IamRepoFn)
if err != nil {
return fmt.Errorf("failed to create credential store handler service: %w", err)
}
@@ -230,6 +232,13 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
}
services.RegisterWorkerServiceServer(s, ws)
}
if _, ok := currentServices[services.CredentialService_ServiceDesc.ServiceName]; !ok {
c, err := credentials.NewService(c.StaticCredentialRepoFn, c.IamRepoFn)
if err != nil {
return fmt.Errorf("failed to create credential handler service: %w", err)
}
services.RegisterCredentialServiceServer(s, c)
}
if _, ok := s.GetServiceInfo()[opsservices.HealthService_ServiceDesc.ServiceName]; !ok {
hs := health.NewService()
opsservices.RegisterHealthServiceServer(s, hs)
@@ -290,6 +299,9 @@ func registerGrpcGatewayEndpoints(ctx context.Context, gwMux *runtime.ServeMux,
if err := services.RegisterWorkerServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil {
return fmt.Errorf("failed to register worker service handler: %w", err)
}
if err := services.RegisterCredentialServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil {
return fmt.Errorf("failed to register credential service handler: %w", err)
}
return nil
}
@@ -163,6 +163,12 @@ func TestHandleImplementedPaths(t *testing.T) {
"v1/targets/someid:add-host-sets",
"v1/targets/someid:set-host-sets",
"v1/targets/someid:remove-host-sets",
"v1/targets/someid:add-host-sources",
"v1/targets/someid:set-host-sources",
"v1/targets/someid:remove-host-sources",
"v1/targets/someid:add-credential-sources",
"v1/targets/someid:set-credential-sources",
"v1/targets/someid:remove-credential-sources",
"v1/users/someid:add-accounts",
"v1/users/someid:set-accounts",
"v1/users/someid:remove-accounts",
@@ -1265,7 +1265,9 @@ func transformAuthenticateRequestAttributes(msg proto.Message) error {
return fmt.Errorf("%s: unknown command %q", op, authRequest.GetCommand())
}
default:
return fmt.Errorf("%s: unknown auth method subtype in ID %q", op, authRequest.GetAuthMethodId())
return &subtypes.UnknownSubtypeIDError{
ID: authRequest.GetAuthMethodId(),
}
}
return nil
}
@@ -579,7 +579,7 @@ func toStorageVaultLibrary(storeId string, in *pb.CredentialLibrary) (out *vault
credentialType := credential.Type(in.GetCredentialType())
switch credentialType {
case credential.UserPasswordType:
case credential.UsernamePasswordType:
opts = append(opts, vault.WithCredentialType(credentialType))
overrides := in.CredentialMappingOverrides.AsMap()
var mapOpts []vault.Option
@@ -694,7 +694,7 @@ func validateMapping(badFields map[string]string, credentialType credential.Type
badFields[globals.CredentialMappingOverridesField] = fmt.Sprintf("This field can only be set if %q is set", globals.CredentialTypeField)
}
return
case credential.UserPasswordType:
case credential.UsernamePasswordType:
validFields[usernameAttribute] = true
validFields[passwordAttribute] = true
default:
@@ -734,7 +734,7 @@ func getMappingUpdates(credentialType credential.Type, current vault.MappingOver
}
switch credentialType {
case credential.UserPasswordType:
case credential.UsernamePasswordType:
var currentUser, currentPass interface{}
if overrides, ok := current.(*vault.UserPasswordOverride); ok {
currentUser = overrides.UsernameAttribute
@@ -318,7 +318,7 @@ func TestCreate(t *testing.T) {
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Invalid user_password mapping",
name: "Invalid username_password mapping",
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
CredentialStoreId: store.GetPublicId(),
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
@@ -326,7 +326,7 @@ func TestCreate(t *testing.T) {
Path: wrapperspb.String("something"),
},
},
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: "user-test",
@@ -410,7 +410,7 @@ func TestCreate(t *testing.T) {
},
},
{
name: "Create a valid vault CredentialLibrary user_password type",
name: "Create a valid vault CredentialLibrary username_password type",
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
CredentialStoreId: store.GetPublicId(),
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
@@ -418,7 +418,7 @@ func TestCreate(t *testing.T) {
Path: wrapperspb.String("something"),
},
},
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
}},
idPrefix: vault.CredentialLibraryPrefix + "_",
res: &pbs.CreateCredentialLibraryResponse{
@@ -437,13 +437,13 @@ func TestCreate(t *testing.T) {
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Create a valid vault CredentialLibrary user_password type with username mapping",
name: "Create a valid vault CredentialLibrary username_password type with username mapping",
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
CredentialStoreId: store.GetPublicId(),
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
@@ -459,7 +459,7 @@ func TestCreate(t *testing.T) {
require.NoError(t, err)
return ret
}(),
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
}},
idPrefix: vault.CredentialLibraryPrefix + "_",
res: &pbs.CreateCredentialLibraryResponse{
@@ -478,7 +478,7 @@ func TestCreate(t *testing.T) {
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: "user-test",
@@ -492,7 +492,7 @@ func TestCreate(t *testing.T) {
},
},
{
name: "Create a valid vault CredentialLibrary user_password type with username/password mapping",
name: "Create a valid vault CredentialLibrary username_password type with username/password mapping",
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
CredentialStoreId: store.GetPublicId(),
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
@@ -509,7 +509,7 @@ func TestCreate(t *testing.T) {
require.NoError(t, err)
return ret
}(),
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
}},
idPrefix: vault.CredentialLibraryPrefix + "_",
res: &pbs.CreateCredentialLibraryResponse{
@@ -528,7 +528,7 @@ func TestCreate(t *testing.T) {
HttpMethod: wrapperspb.String("GET"),
},
},
CredentialType: string(credential.UserPasswordType),
CredentialType: string(credential.UsernamePasswordType),
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: "user-test",
@@ -607,7 +607,7 @@ func TestGet(t *testing.T) {
repo, err := repoFn()
require.NoError(t, err)
lib, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path",
vault.WithCredentialType("user_password"),
vault.WithCredentialType("username_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
vault.WithOverrideUsernameAttribute("user"),
@@ -664,7 +664,7 @@ func TestGet(t *testing.T) {
HttpMethod: wrapperspb.String(userPassLib.GetHttpMethod()),
},
},
CredentialType: "user_password",
CredentialType: "username_password",
CredentialMappingOverrides: func() *structpb.Struct {
v := map[string]interface{}{
usernameAttribute: "user",
@@ -903,7 +903,7 @@ func TestUpdate(t *testing.T) {
{
name: "user-password-attributes-change-username-attribute",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithCredentialType("username_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
@@ -932,7 +932,7 @@ func TestUpdate(t *testing.T) {
{
name: "user-password-attributes-change-password-attribute",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithCredentialType("username_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
@@ -961,7 +961,7 @@ func TestUpdate(t *testing.T) {
{
name: "user-password-attributes-change-username-and-password-attributes",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithCredentialType("username_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
@@ -992,7 +992,7 @@ func TestUpdate(t *testing.T) {
{
name: "no-mapping-override-change-username-and-password-attributes",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithCredentialType("username_password"),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(passwordAttrField, usernameAttrField),
@@ -1023,7 +1023,7 @@ func TestUpdate(t *testing.T) {
{
name: "user-password-attributes-delete-mapping-override",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithCredentialType("username_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
@@ -1045,7 +1045,7 @@ func TestUpdate(t *testing.T) {
{
name: "no-mapping-override-delete-mapping-override",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithCredentialType("username_password"),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(credentialMappingPathField),
@@ -1062,7 +1062,7 @@ func TestUpdate(t *testing.T) {
{
name: "user-password-attributes-delete-mapping-override-field-specific",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithCredentialType("username_password"),
vault.WithMappingOverride(
vault.NewUserPasswordOverride(
vault.WithOverrideUsernameAttribute("orig-user"),
@@ -1092,7 +1092,7 @@ func TestUpdate(t *testing.T) {
{
name: "no-mapping-override-delete-mapping-override-field-specific",
opts: []vault.Option{
vault.WithCredentialType("user_password"),
vault.WithCredentialType("username_password"),
},
req: &pbs.UpdateCredentialLibraryRequest{
UpdateMask: fieldmask(passwordAttrField, usernameAttrField),
@@ -1185,7 +1185,7 @@ func TestUpdate(t *testing.T) {
{
name: "read only credential type",
path: "credential_type",
item: &pb.CredentialLibrary{CredentialType: string(credential.UserPasswordType)},
item: &pb.CredentialLibrary{CredentialType: string(credential.UsernamePasswordType)},
},
}
for _, tc := range errCases {
@@ -0,0 +1,592 @@
package credentials
import (
"context"
"encoding/base64"
"fmt"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/credential/static/store"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/common"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/errors"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/perms"
"github.com/hashicorp/boundary/internal/requests"
"github.com/hashicorp/boundary/internal/types/action"
"github.com/hashicorp/boundary/internal/types/resource"
"github.com/hashicorp/boundary/internal/types/subtypes"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/wrapperspb"
)
const (
usernameField = "attributes.username"
passwordField = "attributes.password"
domain = "credential"
)
var (
maskManager handlers.MaskManager
// IdActions contains the set of actions that can be performed on
// individual resources
IdActions = action.ActionSet{
action.NoOp,
action.Read,
action.Update,
action.Delete,
}
// CollectionActions contains the set of actions that can be performed on
// this collection
CollectionActions = action.ActionSet{
action.Create,
action.List,
}
)
func init() {
var err error
if maskManager, err = handlers.NewMaskManager(handlers.MaskDestination{&store.UsernamePasswordCredential{}},
handlers.MaskSource{&pb.Credential{}, &pb.UsernamePasswordAttributes{}}); err != nil {
panic(err)
}
}
// Service handles request as described by the pbs.CredentialServiceServer interface.
type Service struct {
pbs.UnimplementedCredentialServiceServer
iamRepoFn common.IamRepoFactory
repoFn common.StaticCredentialRepoFactory
}
// NewService returns a credential service which handles credential related requests to boundary.
func NewService(repo common.StaticCredentialRepoFactory, iamRepo common.IamRepoFactory) (Service, error) {
const op = "credentials.NewService"
if iamRepo == nil {
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing iam repository")
}
if repo == nil {
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static credential repository")
}
return Service{iamRepoFn: iamRepo, repoFn: repo}, nil
}
var _ pbs.CredentialServiceServer = Service{}
// ListCredentials implements the interface pbs.CredentialServiceServer
func (s Service) ListCredentials(ctx context.Context, req *pbs.ListCredentialsRequest) (*pbs.ListCredentialsResponse, error) {
if err := validateListRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetCredentialStoreId(), action.List)
if authResults.Error != nil {
return nil, authResults.Error
}
creds, err := s.listFromRepo(ctx, req.GetCredentialStoreId())
if err != nil {
return nil, err
}
if len(creds) == 0 {
return &pbs.ListCredentialsResponse{}, nil
}
filter, err := handlers.NewFilter(req.GetFilter())
if err != nil {
return nil, err
}
finalItems := make([]*pb.Credential, 0, len(creds))
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.Credential,
Pin: req.GetCredentialStoreId(),
}
for _, item := range creds {
res.Id = item.GetPublicId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
continue
}
outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId)
outputOpts := make([]handlers.Option, 0, 3)
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
}
item, err := toProto(item, outputOpts...)
if err != nil {
return nil, err
}
filterable, err := subtypes.Filterable(item)
if err != nil {
return nil, err
}
if filter.Match(filterable) {
finalItems = append(finalItems, item)
}
}
return &pbs.ListCredentialsResponse{Items: finalItems}, nil
}
// GetCredential implements the interface pbs.CredentialServiceServer.
func (s Service) GetCredential(ctx context.Context, req *pbs.GetCredentialRequest) (*pbs.GetCredentialResponse, error) {
const op = "credentials.(Service).GetCredential"
if err := validateGetRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.Read)
if authResults.Error != nil {
return nil, authResults.Error
}
c, err := s.getFromRepo(ctx, req.GetId())
if err != nil {
return nil, err
}
outputFields, ok := requests.OutputFields(ctx)
if !ok {
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
}
outputOpts := make([]handlers.Option, 0, 3)
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, c.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(c, outputOpts...)
if err != nil {
return nil, err
}
return &pbs.GetCredentialResponse{Item: item}, nil
}
// CreateCredential implements the interface pbs.CredentialServiceServer.
func (s Service) CreateCredential(ctx context.Context, req *pbs.CreateCredentialRequest) (*pbs.CreateCredentialResponse, error) {
const op = "credentials.(Service).CreateCredential"
if err := validateCreateRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetItem().GetCredentialStoreId(), action.Create)
if authResults.Error != nil {
return nil, authResults.Error
}
cl, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem())
if err != nil {
return nil, err
}
outputFields, ok := requests.OutputFields(ctx)
if !ok {
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
}
outputOpts := make([]handlers.Option, 0, 3)
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cl.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(cl, outputOpts...)
if err != nil {
return nil, err
}
return &pbs.CreateCredentialResponse{
Item: item,
Uri: fmt.Sprintf("credentials/%s", item.GetId()),
}, nil
}
// UpdateCredential implements the interface pbs.CredentialServiceServer.
func (s Service) UpdateCredential(ctx context.Context, req *pbs.UpdateCredentialRequest) (*pbs.UpdateCredentialResponse, error) {
const op = "credentials.(Service).UpdateCredential"
cur, err := s.getFromRepo(ctx, req.GetId())
if err != nil {
return nil, err
}
storeId := cur.GetStoreId()
if err := validateUpdateRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.Update)
if authResults.Error != nil {
return nil, authResults.Error
}
c, err := s.updateInRepo(ctx, authResults.Scope.GetId(), storeId, req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
if err != nil {
return nil, err
}
outputFields, ok := requests.OutputFields(ctx)
if !ok {
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
}
outputOpts := make([]handlers.Option, 0, 3)
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, c.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(c, outputOpts...)
if err != nil {
return nil, err
}
return &pbs.UpdateCredentialResponse{Item: item}, nil
}
// DeleteCredential implements the interface pbs.CredentialServiceServer.
func (s Service) DeleteCredential(ctx context.Context, req *pbs.DeleteCredentialRequest) (*pbs.DeleteCredentialResponse, error) {
if err := validateDeleteRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.Delete)
if authResults.Error != nil {
return nil, authResults.Error
}
_, err := s.deleteFromRepo(ctx, authResults.Scope.GetId(), req.GetId())
if err != nil {
return nil, err
}
return nil, nil
}
func (s Service) listFromRepo(ctx context.Context, storeId string) ([]credential.Static, error) {
const op = "credentials.(Service).listFromRepo"
repo, err := s.repoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
up, err := repo.ListCredentials(ctx, storeId)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
creds := make([]credential.Static, 0, len(up))
for _, s := range up {
creds = append(creds, s)
}
return creds, nil
}
func (s Service) getFromRepo(ctx context.Context, id string) (credential.Static, error) {
const op = "credentials.(Service).getFromRepo"
repo, err := s.repoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
cred, err := repo.LookupCredential(ctx, id)
if err != nil && !errors.IsNotFoundError(err) {
return nil, errors.Wrap(ctx, err, op)
}
if cred == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("credential %q not found", id))
}
return cred, err
}
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Credential) (credential.Static, error) {
const op = "credentials.(Service).createInRepo"
switch item.GetType() {
case static.UsernamePasswordSubtype.String():
cred, err := toUsernamePasswordStorageCredential(item.GetCredentialStoreId(), item)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
repo, err := s.repoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out, err := repo.CreateUsernamePasswordCredential(ctx, scopeId, cred)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential"))
}
if out == nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential but no error returned from repository.")
}
return out, nil
default:
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, fmt.Sprintf("Unsupported credential type %q", item.GetType()))
}
}
func (s Service) updateInRepo(
ctx context.Context,
scopeId, storeId, id string,
masks []string,
in *pb.Credential,
) (credential.Static, error) {
const op = "credentials.(Service).updateInRepo"
var dbMasks []string
item := proto.Clone(in).(*pb.Credential)
dbMasks = append(dbMasks, maskManager.Translate(masks)...)
if len(dbMasks) == 0 {
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
}
switch subtypes.SubtypeFromId(domain, id) {
case static.UsernamePasswordSubtype:
cred, err := toUsernamePasswordStorageCredential(storeId, in)
cred.PublicId = id
repo, err := s.repoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out, rowsUpdated, err := repo.UpdateUsernamePasswordCredential(ctx, scopeId, cred, item.GetVersion(), dbMasks)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential"))
}
if rowsUpdated == 0 {
return nil, handlers.NotFoundErrorf("Credential %q doesn't exist or incorrect version provided.", id)
}
return out, nil
default:
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, fmt.Sprintf("Unsupported credential type %q", item.GetType()))
}
}
func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) {
const op = "credentials.(Service).deleteFromRepo"
repo, err := s.repoFn()
if err != nil {
return false, err
}
rows, err := repo.DeleteCredential(ctx, scopeId, id)
if err != nil {
if errors.IsNotFoundError(err) {
return false, nil
}
return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential"))
}
return rows > 0, nil
}
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
const op = "credentials.(Service).authResult"
res := auth.VerifyResults{}
repo, err := s.repoFn()
if err != nil {
res.Error = err
return res
}
var parentId string
opts := []auth.Option{auth.WithType(resource.Credential), auth.WithAction(a)}
switch a {
case action.List, action.Create:
parentId = id
default:
opts = append(opts, auth.WithId(id))
cred, err := repo.LookupCredential(ctx, id)
if err != nil {
res.Error = err
return res
}
if cred == nil {
res.Error = handlers.NotFoundError()
return res
}
parentId = cred.GetStoreId()
}
if parentId == "" {
res.Error = errors.New(ctx, errors.RecordNotFound, op, "unable to find credential store for provided credential")
return res
}
opts = append(opts, auth.WithPin(parentId))
cs, err := repo.LookupCredentialStore(ctx, parentId)
if err != nil {
res.Error = err
return res
}
if cs == nil {
res.Error = handlers.NotFoundError()
return res
}
opts = append(opts, auth.WithScopeId(cs.GetScopeId()))
return auth.Verify(ctx, opts...)
}
func toProto(in credential.Static, opt ...handlers.Option) (*pb.Credential, error) {
opts := handlers.GetOpts(opt...)
if opts.WithOutputFields == nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building credential proto")
}
outputFields := *opts.WithOutputFields
out := pb.Credential{}
if outputFields.Has(globals.IdField) {
out.Id = in.GetPublicId()
}
if outputFields.Has(globals.CredentialStoreIdField) {
out.CredentialStoreId = in.GetStoreId()
}
if outputFields.Has(globals.TypeField) {
switch in.(type) {
case *static.UsernamePasswordCredential:
out.Type = static.UsernamePasswordSubtype.String()
}
}
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
out.Description = wrapperspb.String(in.GetDescription())
}
if outputFields.Has(globals.NameField) && in.GetName() != "" {
out.Name = wrapperspb.String(in.GetName())
}
if outputFields.Has(globals.CreatedTimeField) {
out.CreatedTime = in.GetCreateTime().GetTimestamp()
}
if outputFields.Has(globals.UpdatedTimeField) {
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
}
if outputFields.Has(globals.VersionField) {
out.Version = in.GetVersion()
}
if outputFields.Has(globals.ScopeField) {
out.Scope = opts.WithScope
}
if outputFields.Has(globals.AuthorizedActionsField) {
out.AuthorizedActions = opts.WithAuthorizedActions
}
switch cred := in.(type) {
case *static.UsernamePasswordCredential:
if outputFields.Has(globals.AttributesField) {
out.Attrs = &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String(cred.GetUsername()),
PasswordHmac: base64.RawURLEncoding.EncodeToString(cred.GetPasswordHmac()),
},
}
}
}
return &out, nil
}
func toUsernamePasswordStorageCredential(storeId string, in *pb.Credential) (out *static.UsernamePasswordCredential, err error) {
const op = "credentials.toStorageCredential"
var opts []static.Option
if in.GetName() != nil {
opts = append(opts, static.WithName(in.GetName().GetValue()))
}
if in.GetDescription() != nil {
opts = append(opts, static.WithDescription(in.GetDescription().GetValue()))
}
attrs := in.GetUsernamePasswordAttributes()
cs, err := static.NewUsernamePasswordCredential(
storeId,
attrs.GetUsername().GetValue(),
credential.Password(attrs.GetPassword().GetValue()),
opts...)
if err != nil {
return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to build credential"))
}
return cs, err
}
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
// requirements on the structure of the request. They verify that:
// * The path passed in is correctly formatted
// * All required parameters are set
// * There are no conflicting parameters provided
func validateGetRequest(req *pbs.GetCredentialRequest) error {
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.CredentialPrefix)
}
func validateCreateRequest(req *pbs.CreateCredentialRequest) error {
return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string {
badFields := map[string]string{}
if req.Item.GetType() != static.UsernamePasswordSubtype.String() {
badFields[globals.TypeField] = fmt.Sprintf("Unsupported credential type %q", req.Item.GetType())
}
if req.Item.GetCredentialStoreId() == "" {
badFields[globals.CredentialStoreIdField] = "This field must be a valid credential store id."
}
if req.Item.GetUsernamePasswordAttributes().GetUsername().GetValue() == "" {
badFields[usernameField] = "Field required for creating a username-password credential."
}
if req.Item.GetUsernamePasswordAttributes().GetPassword().GetValue() == "" {
badFields[passwordField] = "Field required for creating a username-password credential."
}
return badFields
})
}
func validateUpdateRequest(req *pbs.UpdateCredentialRequest) error {
return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
badFields := map[string]string{}
if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.UsernamePasswordSubtype.String() {
badFields[globals.TypeField] = "Cannot modify resource type."
}
attrs := req.GetItem().GetUsernamePasswordAttributes()
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), usernameField) && attrs.GetUsername().GetValue() == "" {
badFields[usernameField] = "This is a required field and cannot be set to empty."
}
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), passwordField) && attrs.GetPassword().GetValue() == "" {
badFields[passwordField] = "This is a required field and cannot be set to empty."
}
return badFields
}, static.CredentialPrefix)
}
func validateDeleteRequest(req *pbs.DeleteCredentialRequest) error {
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.CredentialPrefix)
}
func validateListRequest(req *pbs.ListCredentialsRequest) error {
badFields := map[string]string{}
if !handlers.ValidId(handlers.Id(req.GetCredentialStoreId()), static.CredentialStorePrefix) {
badFields[globals.CredentialStoreIdField] = "This field must be a valid credential store id."
}
if _, err := handlers.NewFilter(req.GetFilter()); err != nil {
badFields["filter"] = fmt.Sprintf("This field could not be parsed. %v", err)
}
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields)
}
return nil
}
@@ -0,0 +1,742 @@
package credentials
import (
"context"
"encoding/base64"
"fmt"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/credential/vault"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
"github.com/hashicorp/boundary/internal/scheduler"
"github.com/hashicorp/boundary/internal/types/scope"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/fieldmaskpb"
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
var testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
func TestList(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kkms := kms.TestKms(t, conn, wrapper)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrapper)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(context.Background(), rw, rw, kkms)
}
_, prj := iam.TestScopes(t, iamRepo)
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
storeNoCreds := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
var wantCreds []*pb.Credential
for i := 0; i < 10; i++ {
user := fmt.Sprintf("user-%d", i)
pass := fmt.Sprintf("pass-%d", i)
c := static.TestUsernamePasswordCredential(t, conn, wrapper, user, pass, store.GetPublicId(), prj.GetPublicId())
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase)
require.NoError(t, err)
hm, err := crypto.HmacSha256(context.Background(), []byte(pass), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
require.NoError(t, err)
wantCreds = append(wantCreds, &pb.Credential{
Id: c.GetPublicId(),
CredentialStoreId: store.GetPublicId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
CreatedTime: c.GetCreateTime().GetTimestamp(),
UpdatedTime: c.GetUpdateTime().GetTimestamp(),
Version: c.GetVersion(),
Type: static.UsernamePasswordSubtype.String(),
AuthorizedActions: testAuthorizedActions,
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String(c.GetUsername()),
PasswordHmac: base64.RawURLEncoding.EncodeToString([]byte(hm)),
},
},
})
}
cases := []struct {
name string
req *pbs.ListCredentialsRequest
res *pbs.ListCredentialsResponse
anonRes *pbs.ListCredentialsResponse
err error
}{
{
name: "List many credentials",
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId()},
res: &pbs.ListCredentialsResponse{Items: wantCreds},
anonRes: &pbs.ListCredentialsResponse{Items: wantCreds},
},
{
name: "List no credentials",
req: &pbs.ListCredentialsRequest{CredentialStoreId: storeNoCreds.GetPublicId()},
res: &pbs.ListCredentialsResponse{},
anonRes: &pbs.ListCredentialsResponse{},
},
{
name: "Filter to one credential",
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantCreds[1].GetId())},
res: &pbs.ListCredentialsResponse{Items: wantCreds[1:2]},
anonRes: &pbs.ListCredentialsResponse{Items: wantCreds[1:2]},
},
{
name: "Filter on Attribute",
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/username"==%q`, wantCreds[2].GetUsernamePasswordAttributes().GetUsername().Value)},
res: &pbs.ListCredentialsResponse{Items: wantCreds[2:3]},
anonRes: &pbs.ListCredentialsResponse{}, // anonymous user does not have access to attributes
},
{
name: "Filter to no credential",
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
res: &pbs.ListCredentialsResponse{},
anonRes: &pbs.ListCredentialsResponse{},
},
{
name: "Filter Bad Format",
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: `"//id/"=="bad"`},
err: handlers.InvalidArgumentErrorf("bad format", nil),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
s, err := NewService(staticRepoFn, iamRepoFn)
require.NoError(t, err, "Couldn't create new host set service.")
// Test non-anonymous listing
got, gErr := s.ListCredentials(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req)
if tc.err != nil {
require.Error(t, gErr)
assert.True(t, errors.Is(gErr, tc.err), "ListCredentialStore(%q) got error %v, wanted %v", tc.req, gErr, tc.err)
return
}
require.NoError(t, gErr)
assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform(), protocmp.SortRepeated(func(x, y *pb.Credential) bool {
return x.Id < y.Id
})))
// Test anonymous listing
got, gErr = s.ListCredentials(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
require.NoError(t, gErr)
assert.Len(t, got.Items, len(tc.anonRes.Items))
for _, item := range got.GetItems() {
require.Nil(t, item.CreatedTime)
require.Nil(t, item.UpdatedTime)
require.Zero(t, item.Version)
}
})
}
}
func TestGet(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kkms := kms.TestKms(t, conn, wrapper)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrapper)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(context.Background(), rw, rw, kkms)
}
_, prj := iam.TestScopes(t, iamRepo)
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
cred := static.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId())
s, err := NewService(staticRepoFn, iamRepoFn)
require.NoError(t, err)
// Calculate hmac
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase)
require.NoError(t, err)
hm, err := crypto.HmacSha256(context.Background(), []byte("pass"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
require.NoError(t, err)
cases := []struct {
name string
id string
res *pbs.GetCredentialResponse
err error
}{
{
name: "success",
id: cred.GetPublicId(),
res: &pbs.GetCredentialResponse{
Item: &pb.Credential{
Id: cred.GetPublicId(),
CredentialStoreId: cred.GetStoreId(),
Scope: &scopepb.ScopeInfo{Id: store.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Type: static.UsernamePasswordSubtype.String(),
AuthorizedActions: testAuthorizedActions,
CreatedTime: cred.CreateTime.GetTimestamp(),
UpdatedTime: cred.UpdateTime.GetTimestamp(),
Version: 1,
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String("user"),
PasswordHmac: base64.RawURLEncoding.EncodeToString([]byte(hm)),
},
},
},
},
},
{
name: "not found error",
id: fmt.Sprintf("%s_1234567890", static.CredentialPrefix),
err: handlers.NotFoundError(),
},
{
name: "bad prefix",
id: fmt.Sprintf("%s_1234567890", static.CredentialStorePrefix),
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
req := &pbs.GetCredentialRequest{Id: tc.id}
// Test non-anonymous get
got, gErr := s.GetCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), req)
if tc.err != nil {
require.Error(t, gErr)
assert.True(t, errors.Is(gErr, tc.err))
return
}
require.NoError(t, gErr)
assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform()))
// Test anonymous get
got, gErr = s.GetCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), req)
require.NoError(t, gErr)
require.Nil(t, got.Item.CreatedTime)
require.Nil(t, got.Item.UpdatedTime)
require.Zero(t, got.Item.Version)
})
}
}
func TestDelete(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
sche := scheduler.TestScheduler(t, conn, wrapper)
rw := db.New(conn)
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
require.NoError(t, err)
iamRepo := iam.TestRepo(t, conn, wrapper)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(context.Background(), rw, rw, kms)
}
_, prj := iam.TestScopes(t, iamRepo)
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
cred := static.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId())
s, err := NewService(staticRepoFn, iamRepoFn)
require.NoError(t, err)
cases := []struct {
name string
id string
err error
res *pbs.DeleteCredentialResponse
}{
{
name: "success",
id: cred.GetPublicId(),
},
{
name: "not found error",
id: fmt.Sprintf("%s_1234567890", static.CredentialPrefix),
err: handlers.NotFoundError(),
},
{
name: "bad prefix",
id: fmt.Sprintf("%s_1234567890", static.CredentialStorePrefix),
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, gErr := s.DeleteCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), &pbs.DeleteCredentialRequest{Id: tc.id})
assert.EqualValuesf(t, tc.res, got, "DeleteCredentialStore got response %q, wanted %q", got, tc.res)
if tc.err != nil {
require.Error(t, gErr)
assert.True(t, errors.Is(gErr, tc.err))
return
}
require.NoError(t, gErr)
g, err := s.GetCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), &pbs.GetCredentialRequest{Id: tc.id})
assert.Nil(t, g)
assert.True(t, errors.Is(err, handlers.NotFoundError()))
})
}
}
func TestCreate(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kkms := kms.TestKms(t, conn, wrapper)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrapper)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repoFn := func() (*static.Repository, error) {
return static.NewRepository(context.Background(), rw, rw, kkms)
}
_, prj := iam.TestScopes(t, iamRepo)
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
cases := []struct {
name string
req *pbs.CreateCredentialRequest
res *pbs.CreateCredentialResponse
idPrefix string
err error
wantErr bool
}{
{
name: "Can't specify Id",
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
CredentialStoreId: store.GetPublicId(),
Id: static.CredentialPrefix + "_notallowed",
Type: static.UsernamePasswordSubtype.String(),
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String("username"),
Password: wrapperspb.String("password"),
},
},
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Can't specify Created Time",
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
CredentialStoreId: store.GetPublicId(),
CreatedTime: timestamppb.Now(),
Type: static.UsernamePasswordSubtype.String(),
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String("username"),
},
},
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Can't specify Updated Time",
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
CredentialStoreId: store.GetPublicId(),
UpdatedTime: timestamppb.Now(),
Type: static.UsernamePasswordSubtype.String(),
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String("username"),
Password: wrapperspb.String("password"),
},
},
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Must provide type",
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
CredentialStoreId: store.GetPublicId(),
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String("username"),
Password: wrapperspb.String("password"),
},
},
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Must provide username",
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
CredentialStoreId: store.GetPublicId(),
Type: static.UsernamePasswordSubtype.String(),
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Password: wrapperspb.String("password"),
},
},
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Must provide password]",
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
CredentialStoreId: store.GetPublicId(),
Type: static.UsernamePasswordSubtype.String(),
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String("username"),
},
},
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "valid",
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
CredentialStoreId: store.GetPublicId(),
Type: static.UsernamePasswordSubtype.String(),
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String("username"),
Password: wrapperspb.String("password"),
},
},
}},
idPrefix: static.CredentialPrefix + "_",
res: &pbs.CreateCredentialResponse{
Uri: fmt.Sprintf("credentials/%s_", static.CredentialPrefix),
Item: &pb.Credential{
Id: store.GetPublicId(),
CredentialStoreId: store.GetPublicId(),
CreatedTime: store.GetCreateTime().GetTimestamp(),
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
Version: 1,
Type: static.UsernamePasswordSubtype.String(),
AuthorizedActions: testAuthorizedActions,
},
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
s, err := NewService(repoFn, iamRepoFn)
require.NoError(err, "Error when getting new credential store service.")
got, gErr := s.CreateCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req)
if tc.wantErr || tc.err != nil {
require.Error(gErr)
if tc.err != nil {
assert.True(errors.Is(gErr, tc.err), "CreateCredential(...) got error %v, wanted %v", gErr, tc.err)
}
return
}
require.NoError(gErr)
if tc.res == nil {
require.Nil(got)
}
if got != nil {
assert.Contains(got.GetUri(), tc.res.Uri)
assert.True(strings.HasPrefix(got.GetItem().GetId(), tc.idPrefix))
gotCreateTime := got.GetItem().GetCreatedTime()
gotUpdateTime := got.GetItem().GetUpdatedTime()
// Verify it is a credential store created after the test setup's default credential store
assert.True(gotCreateTime.AsTime().After(store.CreateTime.AsTime()), "New credential should have been created after default credential store. Was created %v, which is after %v", gotCreateTime, store.CreateTime.AsTime())
assert.True(gotUpdateTime.AsTime().After(store.CreateTime.AsTime()), "New credential should have been updated after default credential store. Was updated %v, which is after %v", gotUpdateTime, store.CreateTime.AsTime())
// Calculate hmac
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.PublicId, kms.KeyPurposeDatabase)
password := tc.req.GetItem().GetUsernamePasswordAttributes().GetPassword().GetValue()
hm, err := crypto.HmacSha256(context.Background(), []byte(password), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
require.NoError(err)
// Validate attributes equal
assert.Equal(tc.req.GetItem().GetUsernamePasswordAttributes().GetUsername().GetValue(),
got.GetItem().GetUsernamePasswordAttributes().GetUsername().GetValue())
assert.Equal(base64.RawURLEncoding.EncodeToString([]byte(hm)), got.GetItem().GetUsernamePasswordAttributes().GetPasswordHmac())
assert.Empty(got.GetItem().GetUsernamePasswordAttributes().GetPassword())
// Clear attributes for compare below
got.Item.Attrs = nil
// Clear all values which are hard to compare against.
got.Uri, tc.res.Uri = "", ""
got.Item.Id, tc.res.Item.Id = "", ""
got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil
}
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform(), protocmp.SortRepeatedFields(got)), "CreateCredential(%q) got response %q, wanted %q", tc.req, got, tc.res)
})
}
}
func TestUpdate(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kkms := kms.TestKms(t, conn, wrapper)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrapper)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(context.Background(), rw, rw, kkms)
}
_, prj := iam.TestScopes(t, iamRepo)
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
s, err := NewService(staticRepoFn, iamRepoFn)
require.NoError(t, err)
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
return &fieldmaskpb.FieldMask{Paths: paths}
}
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
freshCred := func(user, pass string) (*static.UsernamePasswordCredential, func()) {
t.Helper()
cred := static.TestUsernamePasswordCredential(t, conn, wrapper, user, pass, store.GetPublicId(), prj.GetPublicId())
clean := func() {
_, err := s.DeleteCredential(ctx, &pbs.DeleteCredentialRequest{Id: cred.GetPublicId()})
require.NoError(t, err)
}
return cred, clean
}
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase)
require.NoError(t, err)
successCases := []struct {
name string
req *pbs.UpdateCredentialRequest
res func(cred *pb.Credential) *pb.Credential
}{
{
name: "name",
req: &pbs.UpdateCredentialRequest{
UpdateMask: fieldmask("name"),
Item: &pb.Credential{
Name: wrapperspb.String("new-name"),
},
},
res: func(in *pb.Credential) *pb.Credential {
out := proto.Clone(in).(*pb.Credential)
out.Name = wrapperspb.String("new-name")
return out
},
},
{
name: "description",
req: &pbs.UpdateCredentialRequest{
UpdateMask: fieldmask("description"),
Item: &pb.Credential{
Description: wrapperspb.String("new-description"),
},
},
res: func(in *pb.Credential) *pb.Credential {
out := proto.Clone(in).(*pb.Credential)
out.Description = wrapperspb.String("new-description")
return out
},
},
{
name: "name-and-description",
req: &pbs.UpdateCredentialRequest{
UpdateMask: fieldmask("name", "description"),
Item: &pb.Credential{
Name: wrapperspb.String("new-name"),
Description: wrapperspb.String("new-description"),
},
},
res: func(in *pb.Credential) *pb.Credential {
out := proto.Clone(in).(*pb.Credential)
out.Name = wrapperspb.String("new-name")
out.Description = wrapperspb.String("new-description")
return out
},
},
{
name: "update-username",
req: &pbs.UpdateCredentialRequest{
UpdateMask: fieldmask("attributes.username"),
Item: &pb.Credential{
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String("new-user-name"),
},
},
},
},
res: func(in *pb.Credential) *pb.Credential {
out := proto.Clone(in).(*pb.Credential)
out.GetUsernamePasswordAttributes().Username = wrapperspb.String("new-user-name")
return out
},
},
{
name: "update-password",
req: &pbs.UpdateCredentialRequest{
UpdateMask: fieldmask("attributes.password"),
Item: &pb.Credential{
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Password: wrapperspb.String("new-password"),
},
},
},
},
res: func(in *pb.Credential) *pb.Credential {
out := proto.Clone(in).(*pb.Credential)
hm, err := crypto.HmacSha256(context.Background(), []byte("new-password"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
require.NoError(t, err)
out.GetUsernamePasswordAttributes().PasswordHmac = base64.RawURLEncoding.EncodeToString([]byte(hm))
return out
},
},
{
name: "update-username-and-password",
req: &pbs.UpdateCredentialRequest{
UpdateMask: fieldmask("attributes.username", "attributes.password"),
Item: &pb.Credential{
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String("new-username"),
Password: wrapperspb.String("new-password"),
},
},
},
},
res: func(in *pb.Credential) *pb.Credential {
out := proto.Clone(in).(*pb.Credential)
out.GetUsernamePasswordAttributes().Username = wrapperspb.String("new-username")
hm, err := crypto.HmacSha256(context.Background(), []byte("new-password"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
require.NoError(t, err)
out.GetUsernamePasswordAttributes().PasswordHmac = base64.RawURLEncoding.EncodeToString([]byte(hm))
return out
},
},
}
for _, tc := range successCases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
cred, cleanup := freshCred("user", "pass")
defer cleanup()
if tc.req.Item.GetVersion() == 0 {
tc.req.Item.Version = 1
}
if tc.req.GetId() == "" {
tc.req.Id = cred.GetPublicId()
}
resToChange, err := s.GetCredential(ctx, &pbs.GetCredentialRequest{Id: cred.GetPublicId()})
require.NoError(err)
want := &pbs.UpdateCredentialResponse{Item: tc.res(resToChange.GetItem())}
got, gErr := s.UpdateCredential(ctx, tc.req)
require.NoError(gErr)
require.NotNil(got)
gotUpdateTime := got.GetItem().GetUpdatedTime()
created := cred.GetCreateTime().GetTimestamp()
assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
want.Item.UpdatedTime = got.Item.UpdatedTime
assert.EqualValues(2, got.Item.Version)
want.Item.Version = 2
assert.Empty(cmp.Diff(got, want, protocmp.Transform()))
})
}
// cant update read only fields
cred, cleanup := freshCred("user", "pass")
defer cleanup()
newStore := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
roCases := []struct {
path string
item *pb.Credential
matcher func(t *testing.T, e error) // When not set defaults to checking against InvalidArgument Error
}{
{
path: "type",
item: &pb.Credential{Type: "something"},
},
{
path: "store_id",
item: &pb.Credential{CredentialStoreId: newStore.GetPublicId()},
},
{
path: "updated_time",
item: &pb.Credential{UpdatedTime: timestamppb.Now()},
},
{
path: "created_time",
item: &pb.Credential{UpdatedTime: timestamppb.Now()},
},
{
path: "authorized_actions",
item: &pb.Credential{AuthorizedActions: append(testAuthorizedActions, "another")},
},
}
for _, tc := range roCases {
t.Run(fmt.Sprintf("ReadOnlyField/%s", tc.path), func(t *testing.T) {
req := &pbs.UpdateCredentialRequest{
Id: cred.GetPublicId(),
Item: tc.item,
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{tc.path}},
}
req.Item.Version = cred.Version
got, gErr := s.UpdateCredential(ctx, req)
assert.Error(t, gErr)
matcher := tc.matcher
if matcher == nil {
matcher = func(t *testing.T, e error) {
assert.Truef(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)), "got error %v, wanted invalid argument", gErr)
}
}
matcher(t, gErr)
assert.Nil(t, got)
})
}
}
@@ -8,6 +8,7 @@ import (
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/credential/vault"
"github.com/hashicorp/boundary/internal/credential/vault/store"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
@@ -15,6 +16,7 @@ import (
"github.com/hashicorp/boundary/internal/daemon/controller/common/scopeids"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentials"
"github.com/hashicorp/boundary/internal/errors"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/perms"
@@ -25,6 +27,7 @@ import (
"github.com/hashicorp/boundary/internal/types/subtypes"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentialstores"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
@@ -57,9 +60,12 @@ var (
action.List,
}
collectionTypeMap = map[resource.Type]action.ActionSet{
vaultCollectionTypeMap = map[resource.Type]action.ActionSet{
resource.CredentialLibrary: credentiallibraries.CollectionActions,
}
staticCollectionTypeMap = map[resource.Type]action.ActionSet{
resource.Credential: credentials.CollectionActions,
}
)
func init() {
@@ -74,20 +80,28 @@ func init() {
type Service struct {
pbs.UnimplementedCredentialStoreServiceServer
iamRepoFn common.IamRepoFactory
repoFn common.VaultCredentialRepoFactory
iamRepoFn common.IamRepoFactory
vaultRepoFn common.VaultCredentialRepoFactory
staticRepoFn common.StaticCredentialRepoFactory
}
// NewService returns a credential store service which handles credential store related requests to boundary.
func NewService(repo common.VaultCredentialRepoFactory, iamRepo common.IamRepoFactory) (Service, error) {
func NewService(
vaultRepo common.VaultCredentialRepoFactory,
staticRepo common.StaticCredentialRepoFactory,
iamRepo common.IamRepoFactory,
) (Service, error) {
const op = "credentialstores.NewService"
if iamRepo == nil {
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing iam repository")
}
if repo == nil {
if vaultRepo == nil {
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing vault credential repository")
}
return Service{iamRepoFn: iamRepo, repoFn: repo}, nil
if staticRepo == nil {
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static credential repository")
}
return Service{iamRepoFn: iamRepo, vaultRepoFn: vaultRepo, staticRepoFn: staticRepo}, nil
}
var _ pbs.CredentialStoreServiceServer = Service{}
@@ -155,7 +169,7 @@ func (s Service) ListCredentialStores(ctx context.Context, req *pbs.ListCredenti
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
}
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, item.GetPublicId())
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, item.GetPublicId())
if err != nil {
return nil, err
}
@@ -208,7 +222,7 @@ func (s Service) GetCredentialStore(ctx context.Context, req *pbs.GetCredentialS
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings()))
}
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, cs.GetPublicId())
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId())
if err != nil {
return nil, err
}
@@ -253,7 +267,7 @@ func (s Service) CreateCredentialStore(ctx context.Context, req *pbs.CreateCrede
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings()))
}
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, cs.GetPublicId())
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId())
if err != nil {
return nil, err
}
@@ -301,7 +315,7 @@ func (s Service) UpdateCredentialStore(ctx context.Context, req *pbs.UpdateCrede
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings()))
}
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, cs.GetPublicId())
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId())
if err != nil {
return nil, err
}
@@ -332,74 +346,154 @@ func (s Service) DeleteCredentialStore(ctx context.Context, req *pbs.DeleteCrede
return nil, nil
}
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*vault.CredentialStore, error) {
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]credential.Store, error) {
const op = "credentialstores.(Service).listFromRepo"
repo, err := s.repoFn()
vaultRepo, err := s.vaultRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
csl, err := repo.ListCredentialStores(ctx, scopeIds)
vaultCsl, err := vaultRepo.ListCredentialStores(ctx, scopeIds)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
staticRepo, err := s.staticRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
staticCsl, err := staticRepo.ListCredentialStores(ctx, scopeIds)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
csl := make([]credential.Store, 0, len(staticCsl)+len(vaultCsl))
for _, s := range vaultCsl {
csl = append(csl, s)
}
for _, s := range staticCsl {
csl = append(csl, s)
}
return csl, nil
}
func (s Service) getFromRepo(ctx context.Context, id string) (credential.Store, error) {
const op = "credentialstores.(Service).getFromRepo"
repo, err := s.repoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
switch subtypes.SubtypeFromId(domain, id) {
case vault.Subtype:
repo, err := s.vaultRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
cs, err := repo.LookupCredentialStore(ctx, id)
if err != nil && !errors.IsNotFoundError(err) {
return nil, errors.Wrap(ctx, err, op)
}
if cs != nil {
return cs, nil
}
case static.Subtype:
repo, err := s.staticRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
cs, err := repo.LookupCredentialStore(ctx, id)
if err != nil && !errors.IsNotFoundError(err) {
return nil, errors.Wrap(ctx, err, op)
}
if cs != nil {
return cs, nil
}
}
cs, err := repo.LookupCredentialStore(ctx, id)
if err != nil && !errors.IsNotFoundError(err) {
return nil, errors.Wrap(ctx, err, op)
}
if cs == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("credential store %q not found", id))
}
return cs, err
return nil, handlers.NotFoundErrorf("credential store %q not found", id)
}
func (s Service) createInRepo(ctx context.Context, projId string, item *pb.CredentialStore) (credential.Store, error) {
const op = "credentialstores.(Service).createInRepo"
cs, err := toStorageVaultStore(projId, item)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
switch item.Type {
case vault.Subtype.String():
cs, err := toStorageVaultStore(projId, item)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
repo, err := s.vaultRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out, err := repo.CreateCredentialStore(ctx, cs)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential store"))
}
return out, nil
case static.Subtype.String():
cs, err := toStorageStaticStore(projId, item)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
repo, err := s.staticRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out, err := repo.CreateCredentialStore(ctx, cs)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential store"))
}
return out, nil
default:
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential store, unknown type.")
}
repo, err := s.repoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out, err := repo.CreateCredentialStore(ctx, cs)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential store"))
}
if out == nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential store but no error returned from repository.")
}
return out, nil
}
func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []string, item *pb.CredentialStore) (credential.Store, error) {
const op = "credentialstores.(Service).updateInRepo"
cs, err := toStorageVaultStore(projId, item)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
cs.PublicId = id
var out credential.Store
var rowsUpdated int
dbMask := maskManager.Translate(mask)
if len(dbMask) == 0 {
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
}
repo, err := s.repoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out, rowsUpdated, err := repo.UpdateCredentialStore(ctx, cs, item.GetVersion(), dbMask)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential store"))
switch subtypes.SubtypeFromId(domain, id) {
case vault.Subtype:
cs, err := toStorageVaultStore(projId, item)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
cs.PublicId = id
repo, err := s.vaultRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out, rowsUpdated, err = repo.UpdateCredentialStore(ctx, cs, item.GetVersion(), dbMask)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential store"))
}
case static.Subtype:
cs, err := toStorageStaticStore(projId, item)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
cs.PublicId = id
repo, err := s.staticRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
out, rowsUpdated, err = repo.UpdateCredentialStore(ctx, cs, item.GetVersion(), dbMask)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential store"))
}
}
if rowsUpdated == 0 {
return nil, handlers.NotFoundErrorf("Credential Store %q doesn't exist or incorrect version provided.", id)
@@ -409,16 +503,34 @@ func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []str
func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
const op = "credentialstores.(Service).deleteFromRepo"
repo, err := s.repoFn()
if err != nil {
return false, err
}
rows, err := repo.DeleteCredentialStore(ctx, id)
if err != nil {
if errors.IsNotFoundError(err) {
return false, nil
var rows int
switch subtypes.SubtypeFromId(domain, id) {
case vault.Subtype:
repo, err := s.vaultRepoFn()
if err != nil {
return false, err
}
rows, err = repo.DeleteCredentialStore(ctx, id)
if err != nil {
if errors.IsNotFoundError(err) {
return false, nil
}
return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential store"))
}
case static.Subtype:
repo, err := s.staticRepoFn()
if err != nil {
return false, err
}
rows, err = repo.DeleteCredentialStore(ctx, id)
if err != nil {
if errors.IsNotFoundError(err) {
return false, nil
}
return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential store"))
}
return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential store"))
}
return rows > 0, nil
}
@@ -430,7 +542,12 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
res.Error = err
return res
}
repo, err := s.repoFn()
vaultRepo, err := s.vaultRepoFn()
if err != nil {
res.Error = err
return res
}
staticRepo, err := s.staticRepoFn()
if err != nil {
res.Error = err
return res
@@ -451,16 +568,32 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
return res
}
default:
cs, err := repo.LookupCredentialStore(ctx, id)
if err != nil {
res.Error = err
return res
switch subtypes.SubtypeFromId(domain, id) {
case vault.Subtype:
cs, err := vaultRepo.LookupCredentialStore(ctx, id)
if err != nil {
res.Error = err
return res
}
if cs == nil {
res.Error = handlers.NotFoundError()
return res
}
parentId = cs.GetScopeId()
case static.Subtype:
var err error
cs, err := staticRepo.LookupCredentialStore(ctx, id)
if err != nil {
res.Error = err
return res
}
if cs == nil {
res.Error = handlers.NotFoundError()
return res
}
parentId = cs.GetScopeId()
}
if cs == nil {
res.Error = handlers.NotFoundError()
return res
}
parentId = cs.GetScopeId()
opts = append(opts, auth.WithId(id))
}
opts = append(opts, auth.WithScopeId(parentId))
@@ -550,6 +683,23 @@ func toProto(in credential.Store, opt ...handlers.Option) (*pb.CredentialStore,
return &out, nil
}
func toStorageStaticStore(scopeId string, in *pb.CredentialStore) (out *static.CredentialStore, err error) {
const op = "credentialstores.toStorageStaticStore"
var opts []static.Option
if in.GetName() != nil {
opts = append(opts, static.WithName(in.GetName().GetValue()))
}
if in.GetDescription() != nil {
opts = append(opts, static.WithDescription(in.GetDescription().GetValue()))
}
cs, err := static.NewCredentialStore(scopeId, opts...)
if err != nil {
return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to build credential store for creation"))
}
return cs, err
}
func toStorageVaultStore(scopeId string, in *pb.CredentialStore) (out *vault.CredentialStore, err error) {
const op = "credentialstores.toStorageVaultStore"
var opts []vault.Option
@@ -609,7 +759,7 @@ func toStorageVaultStore(scopeId string, in *pb.CredentialStore) (out *vault.Cre
// * All required parameters are set
// * There are no conflicting parameters provided
func validateGetRequest(req *pbs.GetCredentialStoreRequest) error {
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix)
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix, static.CredentialStorePrefix)
}
func validateCreateRequest(req *pbs.CreateCredentialStoreRequest) error {
@@ -651,6 +801,8 @@ func validateCreateRequest(req *pbs.CreateCredentialStoreRequest) error {
if len(cs) > 0 && pk == nil {
badFields[clientCertField] = "Cannot set a client certificate without a private key."
}
case static.Subtype:
// No additional validation required for static credential store
default:
badFields[globals.TypeField] = "This is a required field and must be a known credential store type."
}
@@ -693,11 +845,11 @@ func validateUpdateRequest(req *pbs.UpdateCredentialStoreRequest) error {
}
}
return badFields
}, vault.CredentialStorePrefix)
}, vault.CredentialStorePrefix, static.CredentialStorePrefix)
}
func validateDeleteRequest(req *pbs.DeleteCredentialStoreRequest) error {
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix)
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix, static.CredentialStorePrefix)
}
func validateListRequest(req *pbs.ListCredentialStoresRequest) error {
@@ -714,3 +866,20 @@ func validateListRequest(req *pbs.ListCredentialStoresRequest) error {
}
return nil
}
func calculateAuthorizedCollectionActions(ctx context.Context, authResults auth.VerifyResults, id string) (map[string]*structpb.ListValue, error) {
var collectionActions map[string]*structpb.ListValue
var err error
switch subtypes.SubtypeFromId(domain, id) {
case vault.Subtype:
collectionActions, err = auth.CalculateAuthorizedCollectionActions(ctx, authResults, vaultCollectionTypeMap, authResults.Scope.Id, id)
case static.Subtype:
collectionActions, err = auth.CalculateAuthorizedCollectionActions(ctx, authResults, staticCollectionTypeMap, authResults.Scope.Id, id)
}
if err != nil {
return nil, err
}
return collectionActions, nil
}
@@ -11,6 +11,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
credstatic "github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/credential/vault"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
@@ -36,8 +37,8 @@ import (
)
var (
testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
testAuthorizedCollectionActions = map[string]*structpb.ListValue{
testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
testAuthorizedVaultCollectionActions = map[string]*structpb.ListValue{
"credential-libraries": {
Values: []*structpb.Value{
structpb.NewStringValue("create"),
@@ -45,6 +46,14 @@ var (
},
},
}
testAuthorizedStaticCollectionActions = map[string]*structpb.ListValue{
"credentials": {
Values: []*structpb.Value{
structpb.NewStringValue("create"),
structpb.NewStringValue("list"),
},
},
}
)
func TestList(t *testing.T) {
@@ -60,9 +69,12 @@ func TestList(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repoFn := func() (*vault.Repository, error) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(context.Background(), rw, rw, kms)
}
_, prjNoStores := iam.TestScopes(t, iamRepo)
_, prj := iam.TestScopes(t, iamRepo)
@@ -78,7 +90,7 @@ func TestList(t *testing.T) {
Version: s.GetVersion(),
Type: vault.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedCollectionActions,
AuthorizedCollectionActions: testAuthorizedVaultCollectionActions,
Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{
VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{
Address: wrapperspb.String(s.GetVaultAddress()),
@@ -91,6 +103,20 @@ func TestList(t *testing.T) {
})
}
for _, s := range credstatic.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 10) {
wantStores = append(wantStores, &pb.CredentialStore{
Id: s.GetPublicId(),
ScopeId: prj.GetPublicId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
CreatedTime: s.GetCreateTime().GetTimestamp(),
UpdatedTime: s.GetUpdateTime().GetTimestamp(),
Version: s.GetVersion(),
Type: credstatic.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
})
}
cases := []struct {
name string
req *pbs.ListCredentialStoresRequest
@@ -111,11 +137,17 @@ func TestList(t *testing.T) {
anonRes: &pbs.ListCredentialStoresResponse{},
},
{
name: "Filter to One Store",
name: "Filter to One Vault Store",
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantStores[1].GetId())},
res: &pbs.ListCredentialStoresResponse{Items: wantStores[1:2]},
anonRes: &pbs.ListCredentialStoresResponse{Items: wantStores[1:2]},
},
{
name: "Filter to One static Store",
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantStores[11].GetId())},
res: &pbs.ListCredentialStoresResponse{Items: wantStores[11:12]},
anonRes: &pbs.ListCredentialStoresResponse{Items: wantStores[11:12]},
},
{
name: "Filter on Attribute",
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/address"==%q`, wantStores[2].GetVaultCredentialStoreAttributes().GetAddress().Value)},
@@ -136,7 +168,7 @@ func TestList(t *testing.T) {
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
s, err := NewService(repoFn, iamRepoFn)
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
require.NoError(t, err, "Couldn't create new host set service.")
// Test non-anonymous listing
@@ -164,7 +196,7 @@ func TestList(t *testing.T) {
}
}
func TestCreate(t *testing.T) {
func TestCreateVault(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
@@ -177,9 +209,12 @@ func TestCreate(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repoFn := func() (*vault.Repository, error) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(context.Background(), rw, rw, kms)
}
_, prj := iam.TestScopes(t, iamRepo)
defaultCreated := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0].GetCreateTime().GetTimestamp()
@@ -343,22 +378,6 @@ func TestCreate(t *testing.T) {
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Can't specify Update Time",
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
UpdatedTime: timestamppb.Now(),
Type: vault.Subtype.String(),
Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{
VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{
Address: wrapperspb.String(v.Addr),
Token: wrapperspb.String(newToken()),
},
},
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Must specify type",
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
@@ -433,7 +452,7 @@ func TestCreate(t *testing.T) {
},
},
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedCollectionActions,
AuthorizedCollectionActions: testAuthorizedVaultCollectionActions,
},
},
},
@@ -474,7 +493,7 @@ func TestCreate(t *testing.T) {
},
},
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedCollectionActions,
AuthorizedCollectionActions: testAuthorizedVaultCollectionActions,
},
},
},
@@ -483,7 +502,7 @@ func TestCreate(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
s, err := NewService(repoFn, iamRepoFn)
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
require.NoError(err, "Error when getting new credential store service.")
defer cleanup(s)
@@ -533,6 +552,171 @@ func TestCreate(t *testing.T) {
}
}
func TestCreateStatic(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
sche := scheduler.TestScheduler(t, conn, wrapper)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrapper)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(context.Background(), rw, rw, kms)
}
_, prj := iam.TestScopes(t, iamRepo)
defaultCreated := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
cleanup := func(s Service) {
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
r, err := s.ListCredentialStores(ctx, &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId()})
require.NoError(t, err)
for _, i := range r.GetItems() {
_, err := s.DeleteCredentialStore(ctx, &pbs.DeleteCredentialStoreRequest{Id: i.GetId()})
require.NoError(t, err)
}
}
cases := []struct {
name string
req *pbs.CreateCredentialStoreRequest
res *pbs.CreateCredentialStoreResponse
idPrefix string
err error
wantErr bool
}{
{
name: "Can't specify Id",
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
Id: credstatic.CredentialStorePrefix + "_notallowed",
Type: credstatic.Subtype.String(),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Can't specify Created Time",
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
CreatedTime: timestamppb.Now(),
Type: credstatic.Subtype.String(),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Can't specify Update Time",
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
UpdatedTime: timestamppb.Now(),
Type: credstatic.Subtype.String(),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Must specify type",
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Create a valid static CredentialStore",
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
Type: credstatic.Subtype.String(),
}},
idPrefix: credstatic.CredentialStorePrefix + "_",
res: &pbs.CreateCredentialStoreResponse{
Uri: fmt.Sprintf("credential-stores/%s_", credstatic.CredentialStorePrefix),
Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
Version: 1,
Type: credstatic.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
},
},
},
{
name: "Create a valid static CredentialStore with name and description",
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
Type: credstatic.Subtype.String(),
}},
idPrefix: credstatic.CredentialStorePrefix + "_",
res: &pbs.CreateCredentialStoreResponse{
Uri: fmt.Sprintf("credential-stores/%s_", credstatic.CredentialStorePrefix),
Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
Version: 1,
Type: credstatic.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
},
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
require.NoError(err, "Error when getting new credential store service.")
defer cleanup(s)
got, gErr := s.CreateCredentialStore(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetItem().GetScopeId()), tc.req)
if tc.wantErr || tc.err != nil {
require.Error(gErr)
if tc.err != nil {
assert.True(errors.Is(gErr, tc.err), "CreateCredentialStore(...) got error %v, wanted %v", gErr, tc.err)
}
return
}
require.NoError(gErr)
if tc.res == nil {
require.Nil(got)
}
cmpOptions := []cmp.Option{
protocmp.Transform(),
protocmp.SortRepeatedFields(got),
}
if got != nil {
assert.Contains(got.GetUri(), tc.res.Uri)
cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pbs.CreateCredentialStoreResponse{}, "uri"))
assert.True(strings.HasPrefix(got.GetItem().GetId(), tc.idPrefix))
cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pb.CredentialStore{}, "id"))
gotCreateTime := got.GetItem().GetCreatedTime()
gotUpdateTime := got.GetItem().GetUpdatedTime()
// Verify it is a credential store created after the test setup's default credential store
assert.True(gotCreateTime.AsTime().After(defaultCreated.CreateTime.AsTime()), "New credential store should have been created after default credential store. Was created %v, which is after %v", gotCreateTime, defaultCreated)
assert.True(gotUpdateTime.AsTime().After(defaultCreated.CreateTime.AsTime()), "New credential store should have been updated after default credential store. Was updated %v, which is after %v", gotUpdateTime, defaultCreated)
cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pb.CredentialStore{}, "created_time"))
cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pb.CredentialStore{}, "updated_time"))
}
assert.Empty(cmp.Diff(got, tc.res, cmpOptions...), "CreateCredentialStore(%q) got response %q, wanted %q", tc.req, got, tc.res)
})
}
}
func TestGet(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
@@ -544,14 +728,18 @@ func TestGet(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repoFn := func() (*vault.Repository, error) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(context.Background(), rw, rw, kms)
}
_, prj := iam.TestScopes(t, iamRepo)
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
s, err := NewService(repoFn, iamRepoFn)
vaultStore := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
require.NoError(t, err)
cases := []struct {
@@ -561,35 +749,57 @@ func TestGet(t *testing.T) {
err error
}{
{
name: "success",
id: store.GetPublicId(),
name: "vault success",
id: vaultStore.GetPublicId(),
res: &pbs.GetCredentialStoreResponse{
Item: &pb.CredentialStore{
Id: store.GetPublicId(),
ScopeId: store.GetScopeId(),
Scope: &scopepb.ScopeInfo{Id: store.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Id: vaultStore.GetPublicId(),
ScopeId: vaultStore.GetScopeId(),
Scope: &scopepb.ScopeInfo{Id: vaultStore.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Type: vault.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedCollectionActions,
CreatedTime: store.CreateTime.GetTimestamp(),
UpdatedTime: store.UpdateTime.GetTimestamp(),
AuthorizedCollectionActions: testAuthorizedVaultCollectionActions,
CreatedTime: vaultStore.CreateTime.GetTimestamp(),
UpdatedTime: vaultStore.UpdateTime.GetTimestamp(),
Version: 1,
Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{
VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{
Address: wrapperspb.String(store.GetVaultAddress()),
TokenHmac: base64.RawURLEncoding.EncodeToString(store.Token().GetTokenHmac()),
ClientCertificate: wrapperspb.String(string(store.ClientCertificate().GetCertificate())),
ClientCertificateKeyHmac: base64.RawURLEncoding.EncodeToString(store.ClientCertificate().GetCertificateKeyHmac()),
Address: wrapperspb.String(vaultStore.GetVaultAddress()),
TokenHmac: base64.RawURLEncoding.EncodeToString(vaultStore.Token().GetTokenHmac()),
ClientCertificate: wrapperspb.String(string(vaultStore.ClientCertificate().GetCertificate())),
ClientCertificateKeyHmac: base64.RawURLEncoding.EncodeToString(vaultStore.ClientCertificate().GetCertificateKeyHmac()),
},
},
},
},
},
{
name: "not found error",
name: "static success",
id: staticStore.GetPublicId(),
res: &pbs.GetCredentialStoreResponse{
Item: &pb.CredentialStore{
Id: staticStore.GetPublicId(),
ScopeId: staticStore.GetScopeId(),
Scope: &scopepb.ScopeInfo{Id: staticStore.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Type: credstatic.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
CreatedTime: staticStore.CreateTime.GetTimestamp(),
UpdatedTime: staticStore.UpdateTime.GetTimestamp(),
Version: 1,
},
},
},
{
name: "vault not found error",
id: fmt.Sprintf("%s_1234567890", vault.CredentialStorePrefix),
err: handlers.NotFoundError(),
},
{
name: "static not found error",
id: fmt.Sprintf("%s_1234567890", credstatic.CredentialStorePrefix),
err: handlers.NotFoundError(),
},
{
name: "bad prefix",
id: fmt.Sprintf("%s_1234567890", static.HostPrefix),
@@ -632,14 +842,18 @@ func TestDelete(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repoFn := func() (*vault.Repository, error) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(context.Background(), rw, rw, kms)
}
_, prj := iam.TestScopes(t, iamRepo)
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)[0]
s, err := NewService(repoFn, iamRepoFn)
vaultStore := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)[0]
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
require.NoError(t, err)
cases := []struct {
@@ -649,14 +863,23 @@ func TestDelete(t *testing.T) {
res *pbs.DeleteCredentialStoreResponse
}{
{
name: "success",
id: store.GetPublicId(),
name: "vault success",
id: vaultStore.GetPublicId(),
},
{
name: "not found error",
name: "static success",
id: staticStore.GetPublicId(),
},
{
name: "vault not found error",
id: fmt.Sprintf("%s_1234567890", vault.CredentialStorePrefix),
err: handlers.NotFoundError(),
},
{
name: "static not found error",
id: fmt.Sprintf("%s_1234567890", credstatic.CredentialStorePrefix),
err: handlers.NotFoundError(),
},
{
name: "bad prefix",
id: fmt.Sprintf("%s_1234567890", static.HostPrefix),
@@ -680,7 +903,7 @@ func TestDelete(t *testing.T) {
}
}
func TestUpdate(t *testing.T) {
func TestUpdateVault(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
@@ -693,14 +916,17 @@ func TestUpdate(t *testing.T) {
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repoFn := func() (*vault.Repository, error) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(context.Background(), rw, rw, kms)
}
_, prj := iam.TestScopes(t, iamRepo)
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
s, err := NewService(repoFn, iamRepoFn)
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
require.NoError(t, err)
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
@@ -1010,3 +1236,178 @@ func TestUpdate(t *testing.T) {
})
}
}
func TestUpdateStatic(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
sche := scheduler.TestScheduler(t, conn, wrapper)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrapper)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(context.Background(), rw, rw, kms)
}
_, prj := iam.TestScopes(t, iamRepo)
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
require.NoError(t, err)
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
return &fieldmaskpb.FieldMask{Paths: paths}
}
freshStore := func() (*credstatic.CredentialStore, func()) {
t.Helper()
st := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
clean := func() {
_, err := s.DeleteCredentialStore(ctx, &pbs.DeleteCredentialStoreRequest{Id: st.GetPublicId()})
require.NoError(t, err)
}
return st, clean
}
successCases := []struct {
name string
req *pbs.UpdateCredentialStoreRequest
res func(*pb.CredentialStore) *pb.CredentialStore
}{
{
name: "name",
req: &pbs.UpdateCredentialStoreRequest{
UpdateMask: fieldmask("name"),
Item: &pb.CredentialStore{
Name: wrapperspb.String("new-name"),
},
},
res: func(in *pb.CredentialStore) *pb.CredentialStore {
out := proto.Clone(in).(*pb.CredentialStore)
out.Name = wrapperspb.String("new-name")
return out
},
},
{
name: "description",
req: &pbs.UpdateCredentialStoreRequest{
UpdateMask: fieldmask("description"),
Item: &pb.CredentialStore{
Description: wrapperspb.String("new-description"),
},
},
res: func(in *pb.CredentialStore) *pb.CredentialStore {
out := proto.Clone(in).(*pb.CredentialStore)
out.Description = wrapperspb.String("new-description")
return out
},
},
{
name: "name-and-description",
req: &pbs.UpdateCredentialStoreRequest{
UpdateMask: fieldmask("name", "description"),
Item: &pb.CredentialStore{
Name: wrapperspb.String("new-name"),
Description: wrapperspb.String("new-description"),
},
},
res: func(in *pb.CredentialStore) *pb.CredentialStore {
out := proto.Clone(in).(*pb.CredentialStore)
out.Name = wrapperspb.String("new-name")
out.Description = wrapperspb.String("new-description")
return out
},
},
}
for _, tc := range successCases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
st, cleanup := freshStore()
defer cleanup()
if tc.req.Item.GetVersion() == 0 {
tc.req.Item.Version = 1
}
if tc.req.GetId() == "" {
tc.req.Id = st.GetPublicId()
}
resToChange, err := s.GetCredentialStore(ctx, &pbs.GetCredentialStoreRequest{Id: st.GetPublicId()})
require.NoError(err)
want := &pbs.UpdateCredentialStoreResponse{Item: tc.res(resToChange.GetItem())}
got, gErr := s.UpdateCredentialStore(ctx, tc.req)
require.NoError(gErr)
require.NotNil(got)
gotUpdateTime := got.GetItem().GetUpdatedTime()
created := st.GetCreateTime().GetTimestamp()
assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
want.Item.UpdatedTime = got.Item.UpdatedTime
assert.EqualValues(2, got.Item.Version)
want.Item.Version = 2
assert.Empty(cmp.Diff(got, want, protocmp.Transform()))
})
}
// cant update read only fields
st, cleanup := freshStore()
defer cleanup()
roCases := []struct {
path string
item *pb.CredentialStore
matcher func(t *testing.T, e error) // When not set defaults to checking against InvalidArgument Error
}{
{
path: "type",
item: &pb.CredentialStore{Type: "something"},
},
{
path: "scope_id",
item: &pb.CredentialStore{ScopeId: "global"},
},
{
path: "updated_time",
item: &pb.CredentialStore{UpdatedTime: timestamppb.Now()},
},
{
path: "created_time",
item: &pb.CredentialStore{UpdatedTime: timestamppb.Now()},
},
{
path: "authorized_actions",
item: &pb.CredentialStore{AuthorizedActions: append(testAuthorizedActions, "another")},
},
}
for _, tc := range roCases {
t.Run(fmt.Sprintf("ReadOnlyField/%s", tc.path), func(t *testing.T) {
req := &pbs.UpdateCredentialStoreRequest{
Id: st.GetPublicId(),
Item: tc.item,
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{tc.path}},
}
req.Item.Version = st.Version
got, gErr := s.UpdateCredentialStore(ctx, req)
assert.Error(t, gErr)
matcher := tc.matcher
if matcher == nil {
matcher = func(t *testing.T, e error) {
assert.Truef(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)), "got error %v, wanted invalid argument", gErr)
}
}
matcher(t, gErr)
assert.Nil(t, got)
})
}
}
@@ -0,0 +1,191 @@
package targets
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/hashicorp/boundary/internal/credential"
credstatic "github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/errors"
serverpb "github.com/hashicorp/boundary/internal/gen/controller/servers/services"
"github.com/hashicorp/boundary/internal/session"
"github.com/hashicorp/boundary/internal/types/subtypes"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)
// dynamicToWorkerCredential converts the strongly typed credential.Dynamic into
// a session.Credential suitable for passing to a Boundary worker.
func dynamicToWorkerCredential(ctx context.Context, cred credential.Dynamic) (session.Credential, error) {
const op = "targets.dynamicToWorkerCredential"
var workerCred *serverpb.Credential
switch c := cred.(type) {
case credential.UsernamePassword:
workerCred = &serverpb.Credential{
Credential: &serverpb.Credential_UserPassword{
UserPassword: &serverpb.UserPassword{
Username: c.Username(),
Password: string(c.Password()),
},
},
}
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
}
data, err := proto.Marshal(workerCred)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling dynamic secret to proto"))
}
return data, nil
}
func dynamicToSessionCredential(ctx context.Context, cred credential.Dynamic) (*pb.SessionCredential, error) {
const op = "targets.dynamicToSessionCredential"
l := cred.Library()
secret := cred.Secret()
// TODO: Access the json directly from the vault response instead of re-marshalling it.
jSecret, err := json.Marshal(secret)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling secret to json"))
}
var sSecret *structpb.Struct
switch secret.(type) {
case map[string]interface{}:
// In this case we actually have to re-decode it. The proto wrappers
// choke on json.Number and at the time I'm writing this I don't
// have time to write a walk function to dig through with reflect
// and find all json.Numbers and replace them. So we eat the
// inefficiency. So note that we are specifically _not_ using a
// decoder with UseNumber here.
var dSecret map[string]interface{}
if err := json.Unmarshal(jSecret, &dSecret); err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("decoding json for proto marshaling"))
}
sSecret, err = structpb.NewStruct(dSecret)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for secret"))
}
}
var credType string
var credData *structpb.Struct
if l.CredentialType() != credential.UnspecifiedType {
credType = string(l.CredentialType())
switch c := cred.(type) {
case credential.UsernamePassword:
credData, err = handlers.ProtoToStruct(
&pb.UserPasswordCredential{
Username: c.Username(),
Password: string(c.Password()),
},
)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
}
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
}
}
return &pb.SessionCredential{
CredentialSource: &pb.CredentialSource{
Id: l.GetPublicId(),
Name: l.GetName(),
Description: l.GetDescription(),
CredentialStoreId: l.GetStoreId(),
Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(),
CredentialType: credType,
},
Secret: &pb.SessionSecret{
Raw: base64.StdEncoding.EncodeToString(jSecret),
Decoded: sSecret,
},
Credential: credData,
}, nil
}
// staticToWorkerCredential converts the credential.Static into
// a session.Credential suitable for passing to a Boundary worker.
func staticToWorkerCredential(ctx context.Context, cred credential.Static) (session.Credential, error) {
const op = "targets.staticToWorkerCredential"
var workerCred *serverpb.Credential
switch c := cred.(type) {
case credstatic.UsernamePasswordCredential:
workerCred = &serverpb.Credential{
Credential: &serverpb.Credential_UserPassword{
UserPassword: &serverpb.UserPassword{
Username: c.GetUsername(),
Password: string(c.GetPassword()),
},
},
}
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
}
data, err := proto.Marshal(workerCred)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling dynamic secret to proto"))
}
return data, nil
}
func staticToSessionCredential(ctx context.Context, cred credential.Static) (*pb.SessionCredential, error) {
const op = "targets.staticToSessionCredential"
var credType string
var credData *structpb.Struct
var secret map[string]interface{}
switch c := cred.(type) {
case *credstatic.UsernamePasswordCredential:
var err error
credType = string(credential.UsernamePasswordType)
credData, err = handlers.ProtoToStruct(
&pb.UserPasswordCredential{
Username: c.GetUsername(),
Password: string(c.GetPassword()),
},
)
secret = map[string]interface{}{
"username": c.GetUsername(),
"password": string(c.GetPassword()),
}
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for static credential"))
}
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
}
jSecret, err := json.Marshal(secret)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling static secret to json"))
}
sSecret, err := structpb.NewStruct(secret)
return &pb.SessionCredential{
CredentialSource: &pb.CredentialSource{
Id: cred.GetPublicId(),
Name: cred.GetName(),
Description: cred.GetDescription(),
CredentialStoreId: cred.GetStoreId(),
Type: credstatic.Subtype.String(),
CredentialType: credType,
},
Secret: &pb.SessionSecret{
Raw: base64.StdEncoding.EncodeToString(jSecret),
Decoded: sSecret,
},
Credential: credData,
}, nil
}
@@ -17,9 +17,15 @@ type Attributes interface {
// Options create target.Options to be used to create a target.Target.
Options() []target.Option
// Vet validates the Attributes and returns a map of fields to error messages
// if any fields are invalid.
Vet() map[string]string
// VetForUpdate validates the Attributes for an updated resource with the
// provided mask paths and returns a map of fields to error messages if any
// fields are invalid.
VetForUpdate([]string) map[string]string
}
type attributeFunc func(interface{}) Attributes
@@ -2,16 +2,17 @@ package targets
import (
"context"
"encoding/base64"
"encoding/json"
stderrors "errors"
"fmt"
"math/rand"
"net"
"net/url"
"strconv"
"strings"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/credential"
credstatic "github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/credential/vault"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/common"
@@ -20,7 +21,6 @@ import (
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
serverpb "github.com/hashicorp/boundary/internal/gen/controller/servers/services"
"github.com/hashicorp/boundary/internal/host"
"github.com/hashicorp/boundary/internal/host/plugin"
"github.com/hashicorp/boundary/internal/host/static"
@@ -41,14 +41,14 @@ import (
"github.com/mr-tron/base58"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
const (
credentialDomain = "credential"
hostDomain = "host"
credentialDomain = "credential"
hostDomain = "host"
missingPortErrStr = "missing port in address"
)
var (
@@ -65,9 +65,6 @@ var (
action.AddHostSources,
action.SetHostSources,
action.RemoveHostSources,
action.AddCredentialLibraries,
action.SetCredentialLibraries,
action.RemoveCredentialLibraries,
action.AddCredentialSources,
action.SetCredentialSources,
action.RemoveCredentialSources,
@@ -93,6 +90,7 @@ type Service struct {
pluginHostRepoFn common.PluginHostRepoFactory
staticHostRepoFn common.StaticRepoFactory
vaultCredRepoFn common.VaultCredentialRepoFactory
staticCredRepoFn common.StaticCredentialRepoFactory
kmsCache *kms.Kms
}
@@ -107,6 +105,7 @@ func NewService(
pluginHostRepoFn common.PluginHostRepoFactory,
staticHostRepoFn common.StaticRepoFactory,
vaultCredRepoFn common.VaultCredentialRepoFactory,
staticCredRepoFn common.StaticCredentialRepoFactory,
) (Service, error) {
const op = "targets.NewService"
if repoFn == nil {
@@ -130,6 +129,9 @@ func NewService(
if vaultCredRepoFn == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing vault credential repository")
}
if staticCredRepoFn == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static credential repository")
}
return Service{
repoFn: repoFn,
iamRepoFn: iamRepoFn,
@@ -138,6 +140,7 @@ func NewService(
pluginHostRepoFn: pluginHostRepoFn,
staticHostRepoFn: staticHostRepoFn,
vaultCredRepoFn: vaultCredRepoFn,
staticCredRepoFn: staticCredRepoFn,
kmsCache: kmsCache,
}, nil
}
@@ -486,7 +489,7 @@ func (s Service) AddTargetHostSources(ctx context.Context, req *pbs.AddTargetHos
if err := validateAddHostSourcesRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.AddHostSets)
authResults := s.authResult(ctx, req.GetId(), action.AddHostSources)
if authResults.Error != nil {
return nil, authResults.Error
}
@@ -524,7 +527,7 @@ func (s Service) SetTargetHostSources(ctx context.Context, req *pbs.SetTargetHos
if err := validateSetHostSourcesRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.SetHostSets)
authResults := s.authResult(ctx, req.GetId(), action.SetHostSources)
if authResults.Error != nil {
return nil, authResults.Error
}
@@ -557,12 +560,12 @@ func (s Service) SetTargetHostSources(ctx context.Context, req *pbs.SetTargetHos
// RemoveTargetHostSources implements the interface pbs.TargetServiceServer.
func (s Service) RemoveTargetHostSources(ctx context.Context, req *pbs.RemoveTargetHostSourcesRequest) (*pbs.RemoveTargetHostSourcesResponse, error) {
const op = "targets.(Service).RemoveTargetHostSets"
const op = "targets.(Service).RemoveTargetHostSources"
if err := validateRemoveHostSourcesRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.RemoveHostSets)
authResults := s.authResult(ctx, req.GetId(), action.RemoveHostSources)
if authResults.Error != nil {
return nil, authResults.Error
}
@@ -593,120 +596,6 @@ func (s Service) RemoveTargetHostSources(ctx context.Context, req *pbs.RemoveTar
return &pbs.RemoveTargetHostSourcesResponse{Item: item}, nil
}
// DEPRECATED: AddTargetCredentialLibraries implements the interface pbs.TargetServiceServer.
func (s Service) AddTargetCredentialLibraries(ctx context.Context, req *pbs.AddTargetCredentialLibrariesRequest) (*pbs.AddTargetCredentialLibrariesResponse, error) {
const op = "targets.(Service).AddTargetCredentialLibraries"
if err := validateAddLibrariesRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.AddCredentialLibraries)
if authResults.Error != nil {
return nil, authResults.Error
}
t, ts, cl, err := s.addCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion())
if err != nil {
return nil, err
}
outputFields, ok := requests.OutputFields(ctx)
if !ok {
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
}
outputOpts := make([]handlers.Option, 0, 3)
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, t, ts, cl, outputOpts...)
if err != nil {
return nil, err
}
return &pbs.AddTargetCredentialLibrariesResponse{Item: item}, nil
}
// DEPRECATED: SetTargetCredentialLibraries implements the interface pbs.TargetServiceServer.
func (s Service) SetTargetCredentialLibraries(ctx context.Context, req *pbs.SetTargetCredentialLibrariesRequest) (*pbs.SetTargetCredentialLibrariesResponse, error) {
const op = "targets.(Service).SetTargetCredentialLibraries"
if err := validateSetLibrariesRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.SetCredentialLibraries)
if authResults.Error != nil {
return nil, authResults.Error
}
t, ts, cl, err := s.setCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion())
if err != nil {
return nil, err
}
outputFields, ok := requests.OutputFields(ctx)
if !ok {
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
}
outputOpts := make([]handlers.Option, 0, 3)
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, t, ts, cl, outputOpts...)
if err != nil {
return nil, err
}
return &pbs.SetTargetCredentialLibrariesResponse{Item: item}, nil
}
// DEPRECATED: RemoveTargetCredentialLibraries implements the interface pbs.TargetServiceServer.
func (s Service) RemoveTargetCredentialLibraries(ctx context.Context, req *pbs.RemoveTargetCredentialLibrariesRequest) (*pbs.RemoveTargetCredentialLibrariesResponse, error) {
const op = "targets.(Service).RemoveTargetCredentialLibraries"
if err := validateRemoveLibrariesRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.RemoveCredentialLibraries)
if authResults.Error != nil {
return nil, authResults.Error
}
t, ts, cl, err := s.removeCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion())
if err != nil {
return nil, err
}
outputFields, ok := requests.OutputFields(ctx)
if !ok {
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
}
outputOpts := make([]handlers.Option, 0, 3)
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, t, ts, cl, outputOpts...)
if err != nil {
return nil, err
}
return &pbs.RemoveTargetCredentialLibrariesResponse{Item: item}, nil
}
// AddTargetCredentialSources implements the interface pbs.TargetServiceServer.
func (s Service) AddTargetCredentialSources(ctx context.Context, req *pbs.AddTargetCredentialSourcesRequest) (*pbs.AddTargetCredentialSourcesResponse, error) {
const op = "targets.(Service).AddTargetCredentialSources"
@@ -716,7 +605,12 @@ func (s Service) AddTargetCredentialSources(ctx context.Context, req *pbs.AddTar
}
authResults := s.authResult(ctx, req.GetId(), action.AddCredentialSources)
if authResults.Error != nil {
return nil, authResults.Error
// TODO AddCredentialLibraries was deprecated but grant actions were never migrated
// remove this check once actions have been migrated
authResults = s.authResult(ctx, req.GetId(), action.AddCredentialLibraries)
if authResults.Error != nil {
return nil, authResults.Error
}
}
t, ts, cl, err := s.addCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion())
if err != nil {
@@ -754,7 +648,12 @@ func (s Service) SetTargetCredentialSources(ctx context.Context, req *pbs.SetTar
}
authResults := s.authResult(ctx, req.GetId(), action.SetCredentialSources)
if authResults.Error != nil {
return nil, authResults.Error
// TODO SetCredentialLibraries was deprecated but grant actions were never migrated
// remove this check once actions have been migrated
authResults = s.authResult(ctx, req.GetId(), action.SetCredentialLibraries)
if authResults.Error != nil {
return nil, authResults.Error
}
}
t, ts, cl, err := s.setCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion())
if err != nil {
@@ -792,7 +691,12 @@ func (s Service) RemoveTargetCredentialSources(ctx context.Context, req *pbs.Rem
}
authResults := s.authResult(ctx, req.GetId(), action.RemoveCredentialSources)
if authResults.Error != nil {
return nil, authResults.Error
// TODO RemoveCredentialLibraries was deprecated but grant actions were never migrated
// remove this check once actions have been migrated
authResults = s.authResult(ctx, req.GetId(), action.RemoveCredentialLibraries)
if authResults.Error != nil {
return nil, authResults.Error
}
}
t, ts, cl, err := s.removeCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion())
if err != nil {
@@ -869,7 +773,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
if err != nil {
return nil, err
}
t, hostSources, libs, err := repo.LookupTarget(ctx, t.GetPublicId())
t, hostSources, credSources, err := repo.LookupTarget(ctx, t.GetPublicId())
if err != nil {
if errors.IsNotFoundError(err) {
return nil, handlers.NotFoundErrorf("Target %q not found.", t.GetPublicId())
@@ -978,25 +882,39 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
chosenEndpoint = endpoints[rand.Intn(len(endpoints))]
}
h, p, err := net.SplitHostPort(chosenEndpoint.Address)
switch {
case err != nil && strings.Contains(err.Error(), missingPortErrStr):
if t.GetDefaultPort() == 0 {
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("neither the selected host %q nor the target provides a port to use", chosenEndpoint.HostId))
}
h = chosenEndpoint.Address
p = strconv.FormatUint(uint64(t.GetDefaultPort()), 10)
case err != nil:
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error when parsing the chosen endpoints host address"))
}
// Generate the endpoint URL
endpointUrl := &url.URL{
Scheme: t.GetType().String(),
}
defaultPort := t.GetDefaultPort()
if defaultPort != 0 {
endpointUrl.Host = fmt.Sprintf("%s:%d", chosenEndpoint.Address, defaultPort)
} else {
endpointUrl.Host = chosenEndpoint.Address
Host: net.JoinHostPort(h, p),
}
var reqs []credential.Request
var vaultReqs []credential.Request
var staticIds []string
var dynCreds []*session.DynamicCredential
for _, l := range libs {
reqs = append(reqs, credential.Request{
SourceId: l.Id(),
Purpose: l.CredentialPurpose(),
})
dynCreds = append(dynCreds, session.NewDynamicCredential(l.Id(), l.CredentialPurpose()))
var staticCreds []*session.StaticCredential
for _, cs := range credSources {
switch cs.Type() {
case target.LibraryCredentialSourceType:
vaultReqs = append(vaultReqs, credential.Request{
SourceId: cs.Id(),
Purpose: cs.CredentialPurpose(),
})
dynCreds = append(dynCreds, session.NewDynamicCredential(cs.Id(), cs.CredentialPurpose()))
case target.StaticCredentialSourceType:
staticIds = append(staticIds, cs.Id())
staticCreds = append(staticCreds, session.NewStaticCredential(cs.Id(), cs.CredentialPurpose()))
}
}
expTime := timestamppb.Now()
@@ -1013,6 +931,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
ConnectionLimit: t.GetSessionConnectionLimit(),
WorkerFilter: t.GetWorkerFilter(),
DynamicCredentials: dynCreds,
StaticCredentials: staticCreds,
}
sess, err := session.New(sessionComposition)
@@ -1028,111 +947,82 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
return nil, err
}
var cs []credential.Dynamic
if len(reqs) > 0 {
var dynamic []credential.Dynamic
var staticCredsById map[string]credential.Static
if len(vaultReqs) > 0 {
credRepo, err := s.vaultCredRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
cs, err = credRepo.Issue(ctx, sess.GetPublicId(), reqs)
dynamic, err = credRepo.Issue(ctx, sess.GetPublicId(), vaultReqs)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
}
if len(staticIds) > 0 {
credRepo, err := s.staticCredRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
// Remove duplicate requests
staticIds = strutil.RemoveDuplicates(staticIds, false)
creds, err := credRepo.Retrieve(ctx, t.GetScopeId(), staticIds)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
staticCredsById = make(map[string]credential.Static)
for _, c := range creds {
staticCredsById[c.GetPublicId()] = c
}
}
var creds []*pb.SessionCredential
var workerCreds []session.Credential
for _, cred := range cs {
for _, cred := range dynamic {
switch cred.Purpose() {
case credential.EgressPurpose:
m, err := credentialToProto(ctx, cred)
c, err := dynamicToWorkerCredential(ctx, cred)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
data, err := proto.Marshal(m)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling secret to proto"))
}
workerCreds = append(workerCreds, data)
workerCreds = append(workerCreds, c)
case credential.ApplicationPurpose:
l := cred.Library()
secret := cred.Secret()
// TODO: Access the json directly from the vault response instead of re-marshalling it.
jSecret, err := json.Marshal(secret)
c, err := dynamicToSessionCredential(ctx, cred)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling secret to json"))
return nil, errors.Wrap(ctx, err, op)
}
var sSecret *structpb.Struct
switch secret.(type) {
case map[string]interface{}:
// In this case we actually have to re-decode it. The proto wrappers
// choke on json.Number and at the time I'm writing this I don't
// have time to write a walk function to dig through with reflect
// and find all json.Numbers and replace them. So we eat the
// inefficiency. So note that we are specifically _not_ using a
// decoder with UseNumber here.
var dSecret map[string]interface{}
if err := json.Unmarshal(jSecret, &dSecret); err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("decoding json for proto marshaling"))
}
sSecret, err = structpb.NewStruct(dSecret)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for secret"))
}
}
var credType string
var credData *structpb.Struct
if l.CredentialType() != credential.UnspecifiedType {
credType = string(l.CredentialType())
switch c := cred.(type) {
case credential.UserPassword:
credData, err = handlers.ProtoToStruct(
&pb.UserPasswordCredential{
Username: c.Username(),
Password: string(c.Password()),
},
)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
}
default:
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unsupported credential type"))
}
}
creds = append(creds, &pb.SessionCredential{
CredentialLibrary: &pb.CredentialLibrary{
Id: l.GetPublicId(),
Name: l.GetName(),
Description: l.GetDescription(),
CredentialStoreId: l.GetStoreId(),
Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(),
},
CredentialSource: &pb.CredentialSource{
Id: l.GetPublicId(),
Name: l.GetName(),
Description: l.GetDescription(),
CredentialStoreId: l.GetStoreId(),
Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(),
CredentialType: credType,
},
Secret: &pb.SessionSecret{
Raw: base64.StdEncoding.EncodeToString(jSecret),
Decoded: sSecret,
},
Credential: credData,
})
creds = append(creds, c)
default:
return nil, errors.New(ctx, errors.Unknown, op, fmt.Sprintf("unsupported credential purpose %s", cred.Purpose()))
}
}
for _, sc := range staticCreds {
switch sc.CredentialPurpose {
case string(credential.EgressPurpose):
c, err := staticToWorkerCredential(ctx, staticCredsById[sc.CredentialStaticId])
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
workerCreds = append(workerCreds, c)
case string(credential.ApplicationPurpose):
c, err := staticToSessionCredential(ctx, staticCredsById[sc.CredentialStaticId])
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
creds = append(creds, c)
default:
return nil, errors.New(ctx, errors.Unknown, op, fmt.Sprintf("unsupported credential purpose %s", sc.CredentialPurpose))
}
}
if len(workerCreds) > 0 {
// store credentials in repo, worker will request creds when a connection is established
err = sessionRepo.AddSessionCredentials(ctx, sess.ScopeId, sess.PublicId, workerCreds)
@@ -1166,7 +1056,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
Scope: authResults.Scope,
CreatedTime: sess.CreateTime.GetTimestamp(),
Type: t.GetType().String(),
AuthorizationToken: string(encodedMarshaledSad),
AuthorizationToken: encodedMarshaledSad,
UserId: authResults.UserId,
HostId: chosenEndpoint.HostId,
HostSetId: chosenEndpoint.SetId,
@@ -1267,7 +1157,9 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st
if err != nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build target for update: %v.", err)
}
u.SetPublicId(ctx, id)
if err := u.SetPublicId(ctx, id); err != nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set target id: %v.", err)
}
maskManager, err := subtypeRegistry.maskManager(subtype)
if err != nil {
@@ -1384,12 +1276,16 @@ func (s Service) addCredentialSourcesInRepo(ctx context.Context, targetId string
if err != nil {
return nil, nil, nil, err
}
credLibs, err := createCredLibs(targetId, applicationIds, nil, egressIds)
if err != nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err)
var creds target.CredentialSources
if len(applicationIds) > 0 {
creds.ApplicationCredentialIds = strutil.RemoveDuplicates(applicationIds, false)
}
if len(egressIds) > 0 {
creds.EgressCredentialIds = strutil.RemoveDuplicates(egressIds, false)
}
out, hs, credSources, err := repo.AddTargetCredentialSources(ctx, targetId, version, credLibs)
out, hs, credSources, err := repo.AddTargetCredentialSources(ctx, targetId, version, creds)
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add credential sources to target: %v.", err)
@@ -1407,12 +1303,15 @@ func (s Service) setCredentialSourcesInRepo(ctx context.Context, targetId string
return nil, nil, nil, err
}
credLibs, err := createCredLibs(targetId, applicationIds, nil, egressIds)
if err != nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err)
var ids target.CredentialSources
if len(applicationIds) > 0 {
ids.ApplicationCredentialIds = strutil.RemoveDuplicates(applicationIds, false)
}
if len(egressIds) > 0 {
ids.EgressCredentialIds = strutil.RemoveDuplicates(egressIds, false)
}
_, _, _, err = repo.SetTargetCredentialSources(ctx, targetId, version, credLibs)
_, _, _, err = repo.SetTargetCredentialSources(ctx, targetId, version, ids)
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err)
@@ -1435,11 +1334,14 @@ func (s Service) removeCredentialSourcesInRepo(ctx context.Context, targetId str
return nil, nil, nil, err
}
credLibs, err := createCredLibs(targetId, applicationIds, nil, egressIds)
if err != nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err)
var ids target.CredentialSources
if len(applicationIds) > 0 {
ids.ApplicationCredentialIds = strutil.RemoveDuplicates(applicationIds, false)
}
_, err = repo.DeleteTargetCredentialSources(ctx, targetId, version, credLibs)
if len(egressIds) > 0 {
ids.EgressCredentialIds = strutil.RemoveDuplicates(egressIds, false)
}
_, err = repo.DeleteTargetCredentialSources(ctx, targetId, version, ids)
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove credential sources from target: %v.", err)
@@ -1584,7 +1486,6 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou
var appCredSources, egressCredSources []*pb.CredentialSource
var appCredSourceIds, egressCredSourceIds []string
var appCredLibraries []*pb.CredentialLibrary
for _, cs := range credSources {
switch cs.CredentialPurpose() {
@@ -1595,13 +1496,6 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou
CredentialStoreId: cs.CredentialStoreId(),
})
// ApplicationCredentialLibrariesField is deprecated and should only be populated
// for application purpose
appCredLibraries = append(appCredLibraries, &pb.CredentialLibrary{
Id: cs.Id(),
CredentialStoreId: cs.CredentialStoreId(),
})
case credential.EgressPurpose:
egressCredSources = append(egressCredSources, &pb.CredentialSource{
Id: cs.Id(),
@@ -1614,15 +1508,9 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou
}
}
if outputFields.Has(globals.ApplicationCredentialLibraryIdsField) {
out.ApplicationCredentialLibraryIds = appCredSourceIds
}
if outputFields.Has(globals.ApplicationCredentialSourceIdsField) {
out.ApplicationCredentialSourceIds = appCredSourceIds
}
if outputFields.Has(globals.ApplicationCredentialLibrariesField) {
out.ApplicationCredentialLibraries = appCredLibraries
}
if outputFields.Has(globals.ApplicationCredentialSourcesField) {
out.ApplicationCredentialSources = appCredSources
}
@@ -1702,7 +1590,8 @@ func validateCreateRequest(req *pbs.CreateTargetRequest) error {
func validateUpdateRequest(req *pbs.UpdateTargetRequest) error {
return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
badFields := map[string]string{}
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), globals.NameField) && req.GetItem().GetName().GetValue() == "" {
paths := req.GetUpdateMask().GetPaths()
if handlers.MaskContains(paths, globals.NameField) && req.GetItem().GetName().GetValue() == "" {
badFields[globals.NameField] = "This field cannot be set to empty."
}
if req.GetItem().GetSessionConnectionLimit() != nil {
@@ -1735,7 +1624,7 @@ func validateUpdateRequest(req *pbs.UpdateTargetRequest) error {
if err != nil {
badFields[globals.AttributesField] = "Attribute fields do not match the expected format."
} else {
for k, v := range a.Vet() {
for k, v := range a.VetForUpdate(paths) {
badFields[k] = v
}
}
@@ -1901,72 +1790,6 @@ func validateRemoveHostSourcesRequest(req *pbs.RemoveTargetHostSourcesRequest) e
return nil
}
func validateAddLibrariesRequest(req *pbs.AddTargetCredentialLibrariesRequest) error {
badFields := map[string]string{}
if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) {
badFields[globals.IdField] = "Incorrectly formatted identifier."
}
if req.GetVersion() == 0 {
badFields[globals.VersionField] = "Required field."
}
if len(req.GetApplicationCredentialLibraryIds()) == 0 {
badFields[globals.ApplicationCredentialLibraryIdsField] = "Must be non-empty."
}
for _, cl := range req.GetApplicationCredentialLibraryIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
badFields[globals.ApplicationCredentialLibraryIdsField] = fmt.Sprintf("Incorrectly formatted credential library identifier %q.", cl)
break
}
}
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields)
}
return nil
}
func validateSetLibrariesRequest(req *pbs.SetTargetCredentialLibrariesRequest) error {
badFields := map[string]string{}
if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) {
badFields[globals.IdField] = "Incorrectly formatted identifier."
}
if req.GetVersion() == 0 {
badFields[globals.VersionField] = "Required field."
}
for _, cl := range req.GetApplicationCredentialLibraryIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
badFields[globals.ApplicationCredentialLibraryIdsField] = fmt.Sprintf("Incorrectly formatted credential library identifier %q.", cl)
break
}
}
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields)
}
return nil
}
func validateRemoveLibrariesRequest(req *pbs.RemoveTargetCredentialLibrariesRequest) error {
badFields := map[string]string{}
if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) {
badFields[globals.IdField] = "Incorrectly formatted identifier."
}
if req.GetVersion() == 0 {
badFields[globals.VersionField] = "Required field."
}
if len(req.GetApplicationCredentialLibraryIds()) == 0 {
badFields[globals.ApplicationCredentialLibraryIdsField] = "Must be non-empty."
}
for _, cl := range req.GetApplicationCredentialLibraryIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
badFields[globals.ApplicationCredentialLibraryIdsField] = fmt.Sprintf("Incorrectly formatted credential library identifier %q.", cl)
break
}
}
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields)
}
return nil
}
func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequest) error {
badFields := map[string]string{}
if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) {
@@ -1980,13 +1803,13 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ
badFields[globals.EgressCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided."
}
for _, cl := range req.GetApplicationCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
}
for _, cl := range req.GetEgressCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
@@ -2006,13 +1829,13 @@ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequ
badFields[globals.VersionField] = "Required field."
}
for _, cl := range req.GetApplicationCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
}
for _, cl := range req.GetEgressCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
@@ -2036,13 +1859,13 @@ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourc
badFields[globals.EgressCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided."
}
for _, cl := range req.GetApplicationCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
}
for _, cl := range req.GetEgressCredentialSourceIds() {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
break
}
@@ -2094,45 +1917,6 @@ func validateAuthorizeSessionRequest(req *pbs.AuthorizeSessionRequest) error {
return nil
}
func createCredLibs(targetId string, applicationIds, ingressIds, egressIds []string) ([]*target.CredentialLibrary, error) {
credLibs := make([]*target.CredentialLibrary, 0, len(applicationIds)+len(ingressIds)+len(egressIds))
byPurpose := map[credential.Purpose][]string{
credential.ApplicationPurpose: strutil.RemoveDuplicates(applicationIds, false),
credential.IngressPurpose: strutil.RemoveDuplicates(ingressIds, false),
credential.EgressPurpose: strutil.RemoveDuplicates(egressIds, false),
}
for purpose, ids := range byPurpose {
for _, id := range ids {
l, err := target.NewCredentialLibrary(targetId, id, purpose)
if err != nil {
return nil, err
}
credLibs = append(credLibs, l)
}
}
return credLibs, nil
}
// credentialToProto converts the strongly typed credential.Credential into a known proto.Message.
func credentialToProto(ctx context.Context, cred credential.Credential) (*serverpb.Credential, error) {
const op = "targets.credentialToProto"
switch c := cred.(type) {
case credential.UserPassword:
return &serverpb.Credential{
Credential: &serverpb.Credential_UserPassword{
UserPassword: &serverpb.UserPassword{
Username: c.Username(),
Password: string(c.Password()),
},
},
}, nil
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
}
}
// workerList is a helper type to make the selection of workers clearer and more declarative.
type workerList []*servers.Worker
File diff suppressed because it is too large. Load diff
@@ -10,6 +10,8 @@ import (
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
)
const defaultPortField = "attributes.default_port"
type attribute struct {
*pb.TcpTargetAttributes
}
@@ -24,8 +26,23 @@ func (a *attribute) Options() []target.Option {
func (a *attribute) Vet() map[string]string {
badFields := map[string]string{}
if a.GetDefaultPort() != nil && a.GetDefaultPort().GetValue() == 0 {
badFields["attributes.default_port"] = "This optional field cannot be set to 0."
if a.GetDefaultPort() == nil {
badFields["attributes.default_port"] = "This field is required."
} else if a.GetDefaultPort().GetValue() == 0 {
badFields["attributes.default_port"] = "This field cannot be set to zero."
}
return badFields
}
func (a *attribute) VetForUpdate(p []string) map[string]string {
if !handlers.MaskContains(p, defaultPortField) {
return nil
}
badFields := map[string]string{}
if a.GetDefaultPort() == nil {
badFields["attributes.default_port"] = "This field is required."
} else if a.GetDefaultPort().GetValue() == 0 {
badFields["attributes.default_port"] = "This cannot be set to zero."
}
return badFields
}
@@ -75,17 +75,18 @@ func TestGet(t *testing.T) {
copy(kmsAuthzActions, testAuthorizedActions)
wantKmsWorker := &pb.Worker{
Id: kmsWorker.GetPublicId(),
ScopeId: kmsWorker.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: kmsWorker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
CreatedTime: kmsWorker.CreateTime.GetTimestamp(),
UpdatedTime: kmsWorker.UpdateTime.GetTimestamp(),
Version: kmsWorker.GetVersion(),
Name: wrapperspb.String(kmsWorker.GetName()),
Description: wrapperspb.String(kmsWorker.GetDescription()),
Address: kmsWorker.GetAddress(),
AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()),
LastStatusTime: kmsWorker.GetLastStatusTime().GetTimestamp(),
Id: kmsWorker.GetPublicId(),
ScopeId: kmsWorker.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: kmsWorker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
CreatedTime: kmsWorker.CreateTime.GetTimestamp(),
UpdatedTime: kmsWorker.UpdateTime.GetTimestamp(),
Version: kmsWorker.GetVersion(),
Name: wrapperspb.String(kmsWorker.GetName()),
Description: wrapperspb.String(kmsWorker.GetDescription()),
Address: kmsWorker.GetAddress(),
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()),
LastStatusTime: kmsWorker.GetLastStatusTime().GetTimestamp(),
CanonicalTags: map[string]*structpb.ListValue{
"key": structListValue(t, "val"),
},
@@ -114,17 +115,18 @@ func TestGet(t *testing.T) {
require.NoError(t, err)
wantPkiWorker := &pb.Worker{
Id: pkiWorker.GetPublicId(),
ScopeId: pkiWorker.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: pkiWorker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
CreatedTime: pkiWorker.CreateTime.GetTimestamp(),
UpdatedTime: pkiWorker.UpdateTime.GetTimestamp(),
Version: pkiWorker.GetVersion(),
Name: wrapperspb.String(pkiWorker.GetName()),
Description: wrapperspb.String(pkiWorker.GetDescription()),
Address: pkiWorker.GetAddress(),
AuthorizedActions: testAuthorizedActions,
LastStatusTime: pkiWorker.GetLastStatusTime().GetTimestamp(),
Id: pkiWorker.GetPublicId(),
ScopeId: pkiWorker.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: pkiWorker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
CreatedTime: pkiWorker.CreateTime.GetTimestamp(),
UpdatedTime: pkiWorker.UpdateTime.GetTimestamp(),
Version: pkiWorker.GetVersion(),
Name: wrapperspb.String(pkiWorker.GetName()),
Description: wrapperspb.String(pkiWorker.GetDescription()),
Address: pkiWorker.GetAddress(),
AuthorizedActions: testAuthorizedActions,
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
LastStatusTime: pkiWorker.GetLastStatusTime().GetTimestamp(),
CanonicalTags: map[string]*structpb.ListValue{
"config": structListValue(t, "test"),
},
@@ -208,17 +210,18 @@ func TestList(t *testing.T) {
kmsAuthzActions := make([]string, len(testAuthorizedActions))
copy(kmsAuthzActions, testAuthorizedActions)
wantKmsWorkers = append(wantKmsWorkers, &pb.Worker{
Id: w.GetPublicId(),
ScopeId: w.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
CreatedTime: w.CreateTime.GetTimestamp(),
UpdatedTime: w.UpdateTime.GetTimestamp(),
Version: w.GetVersion(),
Name: wrapperspb.String(w.GetName()),
AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()),
Address: w.GetAddress(),
Type: KmsWorkerType,
LastStatusTime: w.GetLastStatusTime().GetTimestamp(),
Id: w.GetPublicId(),
ScopeId: w.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
CreatedTime: w.CreateTime.GetTimestamp(),
UpdatedTime: w.UpdateTime.GetTimestamp(),
Version: w.GetVersion(),
Name: wrapperspb.String(w.GetName()),
AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()),
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
Address: w.GetAddress(),
Type: KmsWorkerType,
LastStatusTime: w.GetLastStatusTime().GetTimestamp(),
})
}
@@ -226,17 +229,18 @@ func TestList(t *testing.T) {
for i := 0; i < 10; i++ {
w := servers.TestPkiWorker(t, conn, wrap, servers.WithName(fmt.Sprintf("pki-worker%d", i)))
wantPkiWorkers = append(wantPkiWorkers, &pb.Worker{
Id: w.GetPublicId(),
ScopeId: w.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
CreatedTime: w.CreateTime.GetTimestamp(),
UpdatedTime: w.UpdateTime.GetTimestamp(),
Version: w.GetVersion(),
Name: wrapperspb.String(w.GetName()),
AuthorizedActions: testAuthorizedActions,
Address: w.GetAddress(),
Type: PkiWorkerType,
LastStatusTime: w.GetLastStatusTime().GetTimestamp(),
Id: w.GetPublicId(),
ScopeId: w.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
CreatedTime: w.CreateTime.GetTimestamp(),
UpdatedTime: w.UpdateTime.GetTimestamp(),
Version: w.GetVersion(),
Name: wrapperspb.String(w.GetName()),
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
AuthorizedActions: testAuthorizedActions,
Address: w.GetAddress(),
Type: PkiWorkerType,
LastStatusTime: w.GetLastStatusTime().GetTimestamp(),
})
}
@@ -440,15 +444,16 @@ func TestUpdate(t *testing.T) {
},
res: &pbs.UpdateWorkerResponse{
Item: &pb.Worker{
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Name: wrapperspb.String("name"),
Description: wrapperspb.String("desc"),
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Name: wrapperspb.String("name"),
Description: wrapperspb.String("desc"),
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
},
},
},
@@ -465,15 +470,16 @@ func TestUpdate(t *testing.T) {
},
res: &pbs.UpdateWorkerResponse{
Item: &pb.Worker{
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Name: wrapperspb.String("name"),
Description: wrapperspb.String("desc"),
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Name: wrapperspb.String("name"),
Description: wrapperspb.String("desc"),
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
},
},
},
@@ -533,14 +539,15 @@ func TestUpdate(t *testing.T) {
},
res: &pbs.UpdateWorkerResponse{
Item: &pb.Worker{
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Description: wrapperspb.String("default"),
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Description: wrapperspb.String("default"),
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
},
},
},
@@ -556,14 +563,15 @@ func TestUpdate(t *testing.T) {
},
res: &pbs.UpdateWorkerResponse{
Item: &pb.Worker{
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Name: wrapperspb.String("default"),
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Name: wrapperspb.String("default"),
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
},
},
},
@@ -580,15 +588,16 @@ func TestUpdate(t *testing.T) {
},
res: &pbs.UpdateWorkerResponse{
Item: &pb.Worker{
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Name: wrapperspb.String("updated"),
Description: wrapperspb.String("default"),
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Name: wrapperspb.String("updated"),
Description: wrapperspb.String("default"),
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
},
},
},
@@ -605,15 +614,16 @@ func TestUpdate(t *testing.T) {
},
res: &pbs.UpdateWorkerResponse{
Item: &pb.Worker{
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Name: wrapperspb.String("default"),
Description: wrapperspb.String("notignored"),
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
Id: wkr.GetPublicId(),
ScopeId: wkr.GetScopeId(),
Scope: expectedScope,
Name: wrapperspb.String("default"),
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
Description: wrapperspb.String("notignored"),
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Type: PkiWorkerType,
},
},
},
@@ -1235,11 +1245,12 @@ func TestCreateWorkerLed(t *testing.T) {
},
res: &pbs.CreateWorkerLedResponse{
Item: &workers.Worker{
ScopeId: scope.Global.String(),
Name: &wrapperspb.StringValue{Value: "success"},
Description: &wrapperspb.StringValue{Value: "success-description"},
Version: 1,
Type: PkiWorkerType,
ScopeId: scope.Global.String(),
Name: &wrapperspb.StringValue{Value: "success"},
Description: &wrapperspb.StringValue{Value: "success-description"},
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
Version: 1,
Type: PkiWorkerType,
},
},
},
@@ -29,6 +29,7 @@ begin;
create trigger immutable_columns before update on target_credential_library
for each row execute procedure immutable_columns('target_id', 'credential_library_id', 'credential_purpose', 'create_time');
-- replaced in 33/02_target.up.sql
-- target_library provides the store id along with the other data stored in
-- target_credential_library
create view target_library
@@ -2,6 +2,7 @@ begin;
create table credential_type_enm (
name text primary key
-- This constraint is replaced in 32/01_credential_type.up.sql
constraint only_predefined_credential_types_allowed
check (
name in (
@@ -0,0 +1,23 @@
begin;
-- drop constraint so we can migrate user_password to username_password
alter table credential_type_enm
drop constraint only_predefined_credential_types_allowed;
-- Next: we will update user_password to username_password
update credential_type_enm
set name = 'username_password'
where name = 'user_password';
-- Add new constraint that only allows unspecified and new username_password
-- This replaces the constraint defined in 2/02_credential_type.up.sql
alter table credential_type_enm
add constraint only_predefined_credential_types_allowed
check (
name in (
'unspecified',
'username_password'
)
);
commit;
@@ -0,0 +1,118 @@
begin;
create table credential_static_store (
public_id wt_public_id primary key,
scope_id wt_scope_id not null
constraint iam_scope_fkey
references iam_scope (public_id)
on delete cascade
on update cascade,
name wt_name,
description wt_description,
create_time wt_timestamp,
update_time wt_timestamp,
version wt_version,
constraint credential_store_fkey
foreign key (scope_id, public_id)
references credential_store (scope_id, public_id)
on delete cascade
on update cascade,
constraint credential_static_store_scope_id_name_uq
unique(scope_id, name)
);
comment on table credential_static_store is
'credential_static_store is a table where each row is a resource that represents a static credential store. '
'It is a credential_store subtype and an aggregate root.';
create trigger update_version_column after update on credential_static_store
for each row execute procedure update_version_column();
create trigger update_time_column before update on credential_static_store
for each row execute procedure update_time_column();
create trigger default_create_time_column before insert on credential_static_store
for each row execute procedure default_create_time();
create trigger immutable_columns before update on credential_static_store
for each row execute procedure immutable_columns('public_id', 'scope_id','create_time');
create trigger insert_credential_store_subtype before insert on credential_static_store
for each row execute procedure insert_credential_store_subtype();
create trigger delete_credential_store_subtype after delete on credential_static_store
for each row execute procedure delete_credential_store_subtype();
create table credential_static_username_password_credential (
public_id wt_public_id primary key,
store_id wt_public_id not null
constraint credential_static_store_fkey
references credential_static_store (public_id)
on delete cascade
on update cascade,
name wt_name,
description wt_description,
create_time wt_timestamp,
update_time wt_timestamp,
version wt_version,
username text not null
constraint username_must_not_be_empty
check(length(trim(username)) > 0),
password_encrypted bytea not null
constraint password_encrypted_must_not_be_empty
check(length(password_encrypted) > 0),
password_hmac bytea not null
constraint password_hmac_must_not_be_empty
check(length(password_hmac) > 0),
key_id text not null
constraint kms_data_key_version_fkey
references kms_data_key_version (private_id)
on delete restrict
on update cascade,
constraint credential_static_fkey
foreign key (store_id, public_id)
references credential_static (store_id, public_id)
on delete cascade
on update cascade,
constraint credential_static_username_password_credential_store_id_name_uq
unique(store_id, name),
-- The name of this constraint does not follow our naming conventions for
-- unique constraints because it would be to long. The max length for
-- identifiers in PostgreSQL is 63 characters.
-- credential_static_username_password_credential_store_id_public_id_uq
-- is 68 characters.
--
-- https://www.postgresql.org/docs/current/limits.html
constraint credential_static_username_password_store_id_public_id_uq
unique(store_id, public_id)
);
comment on table credential_static_username_password_credential is
'credential_static_username_password_credential is a table where each row is a resource that represents a static username password credential. '
'It is a credential_static subtype and an aggregate root.';
create trigger update_version_column after update on credential_static_username_password_credential
for each row execute procedure update_version_column();
create trigger update_time_column before update on credential_static_username_password_credential
for each row execute procedure update_time_column();
create trigger default_create_time_column before insert on credential_static_username_password_credential
for each row execute procedure default_create_time();
create trigger immutable_columns before update on credential_static_username_password_credential
for each row execute procedure immutable_columns('public_id', 'store_id','create_time');
create trigger insert_credential_static_subtype before insert on credential_static_username_password_credential
for each row execute procedure insert_credential_static_subtype();
create trigger delete_credential_static_subtype after delete on credential_static_username_password_credential
for each row execute procedure delete_credential_static_subtype();
insert into oplog_ticket (name, version)
values
('credential_static_store', 1),
('credential_static_username_password_credential', 1);
commit;
@@ -0,0 +1,82 @@
begin;
-- target_static_credential does not follow the normal table naming convention of target_credential_static
create table target_static_credential (
target_id wt_public_id not null
constraint target_fkey
references target (public_id)
on delete cascade
on update cascade,
credential_static_id wt_public_id not null
constraint credential_static_fkey
references credential_static (public_id)
on delete cascade
on update cascade,
credential_purpose text not null
constraint credential_purpose_enm_fkey
references credential_purpose_enm (name)
on delete restrict
on update cascade,
create_time wt_timestamp,
primary key(target_id, credential_static_id, credential_purpose)
);
comment on table target_static_credential is
'target_static_credential is a join table between the target, credential_static, and credential_purpose_enm tables. '
'A row in the target_static_credential table represents the assignment of a static credential to a target for the specified purpose.';
create trigger default_create_time_column before insert on target_static_credential
for each row execute procedure default_create_time();
create trigger immutable_columns before update on target_static_credential
for each row execute procedure immutable_columns('target_id', 'credential_static_id', 'credential_purpose', 'create_time');
-- replaces view from 10/05_target.up.sql
drop view target_library;
-- target_credential_source provides the store id along with the other data stored in
-- target_credential_library and target_static_credential
create view target_credential_source
as
select
tcl.target_id,
tcl.credential_library_id as credential_source_id,
tcl.credential_purpose,
cl.store_id,
'library' as type
from
target_credential_library tcl,
credential_library cl
where
cl.public_id = tcl.credential_library_id
union
select
tcs.target_id,
tcs.credential_static_id as credential_source_id,
tcs.credential_purpose,
cst.store_id,
'static' as type
from
target_static_credential tcs,
credential_static cst
where
cst.public_id = tcs.credential_static_id;
comment on view target_credential_source is
'target_credential_source is a view where each row contains a credential source and the id of the parent credential store. '
'No encrypted data is returned. This view can be used to retrieve data which will be returned external to boundary.';
create view credential_source_all_types
as
select
public_id,
'library' as type
from
credential_library
union
select
public_id,
'static' as type
from
credential_static;
comment on view credential_source_all_types is
'credential_source_all_types is a view where each row contains the credential source id and type.';
commit;
@@ -0,0 +1,32 @@
begin;
create table session_credential_static (
session_id wt_public_id not null
constraint session_fkey
references session (public_id)
on delete cascade
on update cascade,
credential_static_id wt_public_id
constraint credential_static_fkey
references credential_static (public_id)
on delete cascade
on update cascade,
credential_purpose text not null
constraint credential_purpose_fkey
references credential_purpose_enm (name)
on delete restrict
on update cascade,
primary key(session_id, credential_static_id, credential_purpose),
create_time wt_timestamp
);
comment on table session_credential_dynamic is
'session_credential_static is a join table between the session and static credential tables. '
'It also contains the credential purpose the relationship represents.';
create trigger default_create_time_column before insert on session_credential_static
for each row execute procedure default_create_time();
create trigger immutable_columns before update on session_credential_static
for each row execute procedure immutable_columns('session_id', 'credential_static_id', 'credential_purpose', 'create_time');
commit;
@@ -0,0 +1,111 @@
package oss_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/internal/credential/vault"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/common"
"github.com/hashicorp/boundary/internal/db/schema"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/testing/dbtest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMigrations_user_password_Migration(t *testing.T) {
const (
priorMigration = 31002
currentMigration = 32001
)
t.Parallel()
ctx := context.Background()
dialect := dbtest.Postgres
c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1))
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, c())
})
d, err := common.SqlOpen(dialect, u)
require.NoError(t, err)
// migration to the prior migration (before the one we want to test)
m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}),
))
require.NoError(t, err)
require.NoError(t, m.ApplyMigrations(ctx))
state, err := m.CurrentState(ctx)
require.NoError(t, err)
want := &schema.State{
Initialized: true,
Editions: []schema.EditionState{
{
Name: "oss",
BinarySchemaVersion: priorMigration,
DatabaseSchemaVersion: priorMigration,
DatabaseSchemaState: schema.Equal,
},
},
}
require.Equal(t, want, state)
// get a connection
dbType, err := db.StringToDbType(dialect)
require.NoError(t, err)
conn, err := db.Open(dbType, u)
require.NoError(t, err)
rw := db.New(conn)
rootWrapper := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, rootWrapper)
_, prj := iam.TestScopes(t, iamRepo)
cs, err := vault.NewCredentialStore(prj.PublicId, "https://vault", []byte("token"))
cs.PublicId = "csvlt_test1234"
require.NoError(t, rw.Create(context.Background(), cs))
upLib, err := vault.NewCredentialLibrary(cs.PublicId, "vault_path", vault.WithMethod("GET"), vault.WithCredentialType("user_password"))
upLib.PublicId = "clvlt_testuplib"
require.NoError(t, rw.Create(context.Background(), upLib))
lib, err := vault.NewCredentialLibrary(cs.PublicId, "vault_path", vault.WithMethod("GET"))
lib.PublicId = "clvlt_testlib"
require.NoError(t, rw.Create(context.Background(), lib))
// now we're ready for the migration we want to test.
m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}),
))
require.NoError(t, err)
require.NoError(t, m.ApplyMigrations(ctx))
state, err = m.CurrentState(ctx)
require.NoError(t, err)
want = &schema.State{
Initialized: true,
Editions: []schema.EditionState{
{
Name: "oss",
BinarySchemaVersion: currentMigration,
DatabaseSchemaVersion: currentMigration,
DatabaseSchemaState: schema.Equal,
},
},
}
require.Equal(t, want, state)
// Validate uplib was migrated to username_password
err = rw.LookupByPublicId(context.Background(), upLib)
require.NoError(t, err)
assert.Equal(t, "username_password", upLib.GetCredentialType())
// Validate lib was left as unspecified
err = rw.LookupByPublicId(context.Background(), lib)
require.NoError(t, err)
assert.Equal(t, "unspecified", lib.GetCredentialType())
}
@@ -346,11 +346,11 @@ begin;
values
('vs_______wvs', 'vl______wvl1', 'widget vault library', 'None', '/secrets', 'GET', 'unspecified'),
('vs_______wvs', 'vl______wvl2', 'widget vault ssh', 'None', '/secrets/ssh/admin', 'GET', 'unspecified'),
('vs_______wvs', 'vl______wvl3', 'widget vault kv one', 'None', '/secrets/kv/one', 'GET', 'user_password'),
('vs_______wvs', 'vl______wvl4', 'widget vault kv two', 'None', '/secrets/kv/two', 'GET', 'user_password'),
('vs_______wvs', 'vl______wvl5', 'widget vault kv three', 'None', '/secrets/kv/three', 'GET', 'user_password'),
('vs_______wvs', 'vl______wvl6', 'widget vault kv four', 'None', '/secrets/kv/four', 'GET', 'user_password'),
('vs_______wvs', 'vl______wvl7', 'widget vault kv five', 'None', '/secrets/kv/five', 'GET', 'user_password');
('vs_______wvs', 'vl______wvl3', 'widget vault kv one', 'None', '/secrets/kv/one', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl4', 'widget vault kv two', 'None', '/secrets/kv/two', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl5', 'widget vault kv three', 'None', '/secrets/kv/three', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl6', 'widget vault kv four', 'None', '/secrets/kv/four', 'GET', 'username_password'),
('vs_______wvs', 'vl______wvl7', 'widget vault kv five', 'None', '/secrets/kv/five', 'GET', 'username_password');
insert into credential_vault_library_user_password_mapping_override
(library_id)
@@ -11,19 +11,19 @@ begin;
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl______wvl1' and credential_type = 'unspecified';
select is(count(*), 1::bigint) from credential_library where public_id = 'vl______wvl1' and credential_type = 'unspecified';
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl______wvl3' and credential_type = 'user_password';
select is(count(*), 1::bigint) from credential_library where public_id = 'vl______wvl3' and credential_type = 'user_password';
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl______wvl3' and credential_type = 'username_password';
select is(count(*), 1::bigint) from credential_library where public_id = 'vl______wvl3' and credential_type = 'username_password';
-- validate the insert triggers
prepare insert_vault_library as
insert into credential_vault_library
(store_id, public_id, vault_path, http_method, credential_type)
values
('vs_______wvs', 'vl_______tt1', '/secrets/kv', 'GET', 'user_password');
('vs_______wvs', 'vl_______tt1', '/secrets/kv', 'GET', 'username_password');
select lives_ok('insert_vault_library');
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl_______tt1' and credential_type = 'user_password';
select is(count(*), 1::bigint) from credential_library where public_id = 'vl_______tt1' and credential_type = 'user_password';
select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl_______tt1' and credential_type = 'username_password';
select is(count(*), 1::bigint) from credential_library where public_id = 'vl_______tt1' and credential_type = 'username_password';
-- validate the delete triggers
prepare delete_vault_library as
@@ -26,11 +26,11 @@ begin;
'select_private_libraries',
$$VALUES
('vl______wvl2', 'unspecified', null, null),
('vl______wvl3', 'user_password', null, null),
('vl______wvl4', 'user_password', null, null),
('vl______wvl5', 'user_password', 'my_username', null),
('vl______wvl6', 'user_password', null, 'my_password'),
('vl______wvl7', 'user_password', 'my_username', 'my_password')$$
('vl______wvl3', 'username_password', null, null),
('vl______wvl4', 'username_password', null, null),
('vl______wvl5', 'username_password', 'my_username', null),
('vl______wvl6', 'username_password', null, 'my_password'),
('vl______wvl7', 'username_password', 'my_username', 'my_password')$$
);
-- validate the insert triggers
+246 -213
View File
@@ -20,6 +20,9 @@
{
"name": "controller.api.services.v1.CredentialLibraryService"
},
{
"name": "controller.api.services.v1.CredentialService"
},
{
"name": "controller.api.services.v1.CredentialStoreService"
},
@@ -911,6 +914,147 @@
]
}
},
"/v1/credentials": {
"get": {
"summary": "Lists all Credentials.",
"operationId": "CredentialService_ListCredentials",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/controller.api.services.v1.ListCredentialsResponse"
}
}
},
"parameters": [
{
"name": "credential_store_id",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "filter",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"controller.api.services.v1.CredentialService"
]
},
"post": {
"summary": "Creates a single Credential.",
"operationId": "CredentialService_CreateCredential",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/controller.api.resources.credentials.v1.Credential"
}
}
},
"parameters": [
{
"name": "item",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/controller.api.resources.credentials.v1.Credential"
}
}
],
"tags": [
"controller.api.services.v1.CredentialService"
]
}
},
"/v1/credentials/{id}": {
"get": {
"summary": "Gets a single Credential.",
"operationId": "CredentialService_GetCredential",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/controller.api.resources.credentials.v1.Credential"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
}
],
"tags": [
"controller.api.services.v1.CredentialService"
]
},
"delete": {
"summary": "Deletes a Credential",
"operationId": "CredentialService_DeleteCredential",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/controller.api.services.v1.DeleteCredentialResponse"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
}
],
"tags": [
"controller.api.services.v1.CredentialService"
]
},
"patch": {
"summary": "Updates a Credential.",
"operationId": "CredentialService_UpdateCredential",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/controller.api.resources.credentials.v1.Credential"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "item",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/controller.api.resources.credentials.v1.Credential"
}
},
{
"name": "update_mask",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"controller.api.services.v1.CredentialService"
]
}
},
"/v1/groups": {
"get": {
"summary": "Lists all Groups.",
@@ -2751,52 +2895,6 @@
]
}
},
"/v1/targets/{id}:add-credential-libraries": {
"post": {
"summary": "Deprecated: use AddTargetCredentialSources instead.",
"operationId": "TargetService_AddTargetCredentialLibraries",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/controller.api.resources.targets.v1.Target"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"type": "object",
"properties": {
"version": {
"type": "integer",
"format": "int64",
"description": "Version is used to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version."
},
"application_credential_library_ids": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
],
"tags": [
"controller.api.services.v1.TargetService"
]
}
},
"/v1/targets/{id}:add-credential-sources": {
"post": {
"summary": "Adds existing Credential Sources to a Target.",
@@ -2993,52 +3091,6 @@
]
}
},
"/v1/targets/{id}:remove-credential-libraries": {
"post": {
"summary": "Deprecated: use RemoveTargetCredentialSources instead.",
"operationId": "TargetService_RemoveTargetCredentialLibraries",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/controller.api.resources.targets.v1.Target"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"type": "object",
"properties": {
"version": {
"type": "integer",
"format": "int64",
"description": "Version is used to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version."
},
"application_credential_library_ids": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
],
"tags": [
"controller.api.services.v1.TargetService"
]
}
},
"/v1/targets/{id}:remove-credential-sources": {
"post": {
"summary": "Removes Credential Sources from the Target.",
@@ -3183,53 +3235,6 @@
]
}
},
"/v1/targets/{id}:set-credential-libraries": {
"post": {
"summary": "Deprecated: use SetTargetCredentialSources instead.",
"operationId": "TargetService_SetTargetCredentialLibraries",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/controller.api.resources.targets.v1.Target"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"type": "object",
"properties": {
"version": {
"type": "integer",
"format": "int64",
"description": "Version is used to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version."
},
"application_credential_library_ids": {
"type": "array",
"items": {
"type": "string"
}
}
},
"description": "Sets the values for application_credential_library_ids. If any\ncredential_library_id field that is not set in the request will result in\nthose fields being cleared."
}
}
],
"tags": [
"controller.api.services.v1.TargetService"
]
}
},
"/v1/targets/{id}:set-credential-sources": {
"post": {
"summary": "Sets the Credential Sources on the Target.",
@@ -4098,6 +4103,67 @@
},
"title": "CredentialLibrary contains all fields related to an Credential Library resource"
},
"controller.api.resources.credentials.v1.Credential": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Output only. The ID of the Credential.",
"readOnly": true
},
"credential_store_id": {
"type": "string",
"description": "The ID of the Credential Store of which this Credential is a part."
},
"scope": {
"$ref": "#/definitions/controller.api.resources.scopes.v1.ScopeInfo",
"description": "Output only. Scope information for this Credential.",
"readOnly": true
},
"name": {
"type": "string",
"description": "Optional name for identification purposes."
},
"description": {
"type": "string",
"description": "Optional user-set description for identification purposes."
},
"created_time": {
"type": "string",
"format": "date-time",
"description": "Output only. The time this resource was created.",
"readOnly": true
},
"updated_time": {
"type": "string",
"format": "date-time",
"description": "Output only. The time this resource was last updated.",
"readOnly": true
},
"version": {
"type": "integer",
"format": "int64",
"description": "Version is used in mutation requests, after the initial creation, to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version."
},
"type": {
"type": "string",
"description": "The Credential type."
},
"attributes": {
"type": "object",
"description": "The attributes that are applicable for the specific Credential type."
},
"authorized_actions": {
"type": "array",
"items": {
"type": "string"
},
"description": "Output only. The available actions on this resource for this user.",
"readOnly": true
}
},
"title": "Credential contains all fields related to an Credential resource"
},
"controller.api.resources.credentialstores.v1.CredentialStore": {
"type": "object",
"properties": {
@@ -5046,35 +5112,6 @@
}
}
},
"controller.api.resources.targets.v1.CredentialLibrary": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The ID of the Credential Library."
},
"name": {
"type": "string",
"description": "Output only. The name of the Credential Library.",
"readOnly": true
},
"description": {
"type": "string",
"description": "Output only. The description of the Credential Library.",
"readOnly": true
},
"credential_store_id": {
"type": "string",
"description": "Output only. The Credential Store to which this Credential Library belongs.",
"readOnly": true
},
"type": {
"type": "string",
"description": "Output only. The type of the credential library.",
"readOnly": true
}
}
},
"controller.api.resources.targets.v1.CredentialSource": {
"type": "object",
"properties": {
@@ -5212,11 +5249,6 @@
"description": "Output only. The credential source information.",
"readOnly": true
},
"credential_library": {
"$ref": "#/definitions/controller.api.resources.targets.v1.CredentialLibrary",
"description": "Output only. The library which generated this credential. Deprecated: use credential_source instead.",
"readOnly": true
},
"secret": {
"$ref": "#/definitions/controller.api.resources.targets.v1.SessionSecret",
"description": "Output only. The secret of this credential base64 encoded.",
@@ -5338,22 +5370,6 @@
"type": "string",
"description": "Optional boolean expression to filter the workers that are allowed to satisfy this request."
},
"application_credential_library_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Output only. The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead.",
"readOnly": true
},
"application_credential_libraries": {
"type": "array",
"items": {
"$ref": "#/definitions/controller.api.resources.targets.v1.CredentialLibrary"
},
"description": "Output only. The application credential libraries associated with this Target. Deprecated: use application_credential_sources instead.",
"readOnly": true
},
"application_credential_source_ids": {
"type": "array",
"items": {
@@ -5638,14 +5654,6 @@
}
}
},
"controller.api.services.v1.AddTargetCredentialLibrariesResponse": {
"type": "object",
"properties": {
"item": {
"$ref": "#/definitions/controller.api.resources.targets.v1.Target"
}
}
},
"controller.api.services.v1.AddTargetCredentialSourcesResponse": {
"type": "object",
"properties": {
@@ -5760,6 +5768,17 @@
}
}
},
"controller.api.services.v1.CreateCredentialResponse": {
"type": "object",
"properties": {
"uri": {
"type": "string"
},
"item": {
"$ref": "#/definitions/controller.api.resources.credentials.v1.Credential"
}
}
},
"controller.api.services.v1.CreateCredentialStoreResponse": {
"type": "object",
"properties": {
@@ -5893,6 +5912,9 @@
"controller.api.services.v1.DeleteCredentialLibraryResponse": {
"type": "object"
},
"controller.api.services.v1.DeleteCredentialResponse": {
"type": "object"
},
"controller.api.services.v1.DeleteCredentialStoreResponse": {
"type": "object"
},
@@ -5958,6 +5980,14 @@
}
}
},
"controller.api.services.v1.GetCredentialResponse": {
"type": "object",
"properties": {
"item": {
"$ref": "#/definitions/controller.api.resources.credentials.v1.Credential"
}
}
},
"controller.api.services.v1.GetCredentialStoreResponse": {
"type": "object",
"properties": {
@@ -6109,6 +6139,17 @@
}
}
},
"controller.api.services.v1.ListCredentialsResponse": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/controller.api.resources.credentials.v1.Credential"
}
}
}
},
"controller.api.services.v1.ListGroupsResponse": {
"type": "object",
"properties": {
@@ -6262,14 +6303,6 @@
}
}
},
"controller.api.services.v1.RemoveTargetCredentialLibrariesResponse": {
"type": "object",
"properties": {
"item": {
"$ref": "#/definitions/controller.api.resources.targets.v1.Target"
}
}
},
"controller.api.services.v1.RemoveTargetCredentialSourcesResponse": {
"type": "object",
"properties": {
@@ -6342,14 +6375,6 @@
}
}
},
"controller.api.services.v1.SetTargetCredentialLibrariesResponse": {
"type": "object",
"properties": {
"item": {
"$ref": "#/definitions/controller.api.resources.targets.v1.Target"
}
}
},
"controller.api.services.v1.SetTargetCredentialSourcesResponse": {
"type": "object",
"properties": {
@@ -6406,6 +6431,14 @@
}
}
},
"controller.api.services.v1.UpdateCredentialResponse": {
"type": "object",
"properties": {
"item": {
"$ref": "#/definitions/controller.api.resources.credentials.v1.Credential"
}
}
},
"controller.api.services.v1.UpdateCredentialStoreResponse": {
"type": "object",
"properties": {
@@ -0,0 +1,864 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc (unknown)
// source: controller/api/services/v1/credential_service.proto
package services
import (
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
credentials "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
_ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions"
_ "google.golang.org/genproto/googleapis/api/annotations"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GetCredentialRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *GetCredentialRequest) Reset() {
*x = GetCredentialRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetCredentialRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetCredentialRequest) ProtoMessage() {}
func (x *GetCredentialRequest) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetCredentialRequest.ProtoReflect.Descriptor instead.
func (*GetCredentialRequest) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{0}
}
func (x *GetCredentialRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
type GetCredentialResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Item *credentials.Credential `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
}
func (x *GetCredentialResponse) Reset() {
*x = GetCredentialResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetCredentialResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetCredentialResponse) ProtoMessage() {}
func (x *GetCredentialResponse) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetCredentialResponse.ProtoReflect.Descriptor instead.
func (*GetCredentialResponse) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{1}
}
func (x *GetCredentialResponse) GetItem() *credentials.Credential {
if x != nil {
return x.Item
}
return nil
}
type ListCredentialsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
CredentialStoreId string `protobuf:"bytes,1,opt,name=credential_store_id,json=scope_id,proto3" json:"credential_store_id,omitempty" class:"public"` // @gotags: `class:"public"`
Filter string `protobuf:"bytes,30,opt,name=filter,proto3" json:"filter,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *ListCredentialsRequest) Reset() {
*x = ListCredentialsRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListCredentialsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListCredentialsRequest) ProtoMessage() {}
func (x *ListCredentialsRequest) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListCredentialsRequest.ProtoReflect.Descriptor instead.
func (*ListCredentialsRequest) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{2}
}
func (x *ListCredentialsRequest) GetCredentialStoreId() string {
if x != nil {
return x.CredentialStoreId
}
return ""
}
func (x *ListCredentialsRequest) GetFilter() string {
if x != nil {
return x.Filter
}
return ""
}
type ListCredentialsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Items []*credentials.Credential `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
}
func (x *ListCredentialsResponse) Reset() {
*x = ListCredentialsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListCredentialsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListCredentialsResponse) ProtoMessage() {}
func (x *ListCredentialsResponse) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListCredentialsResponse.ProtoReflect.Descriptor instead.
func (*ListCredentialsResponse) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{3}
}
func (x *ListCredentialsResponse) GetItems() []*credentials.Credential {
if x != nil {
return x.Items
}
return nil
}
type CreateCredentialRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Item *credentials.Credential `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
}
func (x *CreateCredentialRequest) Reset() {
*x = CreateCredentialRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateCredentialRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateCredentialRequest) ProtoMessage() {}
func (x *CreateCredentialRequest) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateCredentialRequest.ProtoReflect.Descriptor instead.
func (*CreateCredentialRequest) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{4}
}
func (x *CreateCredentialRequest) GetItem() *credentials.Credential {
if x != nil {
return x.Item
}
return nil
}
type CreateCredentialResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty" class:"public"` // @gotags: `class:"public"`
Item *credentials.Credential `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"`
}
func (x *CreateCredentialResponse) Reset() {
*x = CreateCredentialResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateCredentialResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateCredentialResponse) ProtoMessage() {}
func (x *CreateCredentialResponse) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateCredentialResponse.ProtoReflect.Descriptor instead.
func (*CreateCredentialResponse) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{5}
}
func (x *CreateCredentialResponse) GetUri() string {
if x != nil {
return x.Uri
}
return ""
}
func (x *CreateCredentialResponse) GetItem() *credentials.Credential {
if x != nil {
return x.Item
}
return nil
}
type UpdateCredentialRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"`
Item *credentials.Credential `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"`
UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=update_mask,proto3" json:"update_mask,omitempty"`
}
func (x *UpdateCredentialRequest) Reset() {
*x = UpdateCredentialRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *UpdateCredentialRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UpdateCredentialRequest) ProtoMessage() {}
func (x *UpdateCredentialRequest) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UpdateCredentialRequest.ProtoReflect.Descriptor instead.
func (*UpdateCredentialRequest) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{6}
}
func (x *UpdateCredentialRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *UpdateCredentialRequest) GetItem() *credentials.Credential {
if x != nil {
return x.Item
}
return nil
}
func (x *UpdateCredentialRequest) GetUpdateMask() *fieldmaskpb.FieldMask {
if x != nil {
return x.UpdateMask
}
return nil
}
type UpdateCredentialResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Item *credentials.Credential `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
}
func (x *UpdateCredentialResponse) Reset() {
*x = UpdateCredentialResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *UpdateCredentialResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UpdateCredentialResponse) ProtoMessage() {}
func (x *UpdateCredentialResponse) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UpdateCredentialResponse.ProtoReflect.Descriptor instead.
func (*UpdateCredentialResponse) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{7}
}
func (x *UpdateCredentialResponse) GetItem() *credentials.Credential {
if x != nil {
return x.Item
}
return nil
}
type DeleteCredentialRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *DeleteCredentialRequest) Reset() {
*x = DeleteCredentialRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeleteCredentialRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteCredentialRequest) ProtoMessage() {}
func (x *DeleteCredentialRequest) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteCredentialRequest.ProtoReflect.Descriptor instead.
func (*DeleteCredentialRequest) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{8}
}
func (x *DeleteCredentialRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
type DeleteCredentialResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *DeleteCredentialResponse) Reset() {
*x = DeleteCredentialResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeleteCredentialResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteCredentialResponse) ProtoMessage() {}
func (x *DeleteCredentialResponse) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteCredentialResponse.ProtoReflect.Descriptor instead.
func (*DeleteCredentialResponse) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{9}
}
var File_controller_api_services_v1_credential_service_proto protoreflect.FileDescriptor
var file_controller_api_services_v1_credential_service_proto_rawDesc = []byte{
0x0a, 0x33, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69,
0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76,
0x31, 0x1a, 0x38, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73,
0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d,
0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x26, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22,
0x60, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31,
0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x04, 0x69, 0x74, 0x65,
0x6d, 0x22, 0x57, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x13, 0x63,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f,
0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x1e, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x64, 0x0a, 0x17, 0x4c, 0x69,
0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e,
0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73,
0x22, 0x62, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x04, 0x69,
0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73,
0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x22, 0x75, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75,
0x72, 0x69, 0x12, 0x47, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb0, 0x01, 0x0a, 0x17,
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x47, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x2e,
0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x12, 0x3c, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73,
0x6b, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x63,
0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x04, 0x69, 0x74,
0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x04, 0x69,
0x74, 0x65, 0x6d, 0x22, 0x29, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e,
0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1a,
0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xbe, 0x07, 0x0a, 0x11, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x12, 0xb6, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e,
0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76,
0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x92, 0x41, 0x1b, 0x12, 0x19, 0x47, 0x65,
0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x43, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x14, 0x2f,
0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x7b,
0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xae, 0x01, 0x0a, 0x0f, 0x4c, 0x69,
0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x32, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x92, 0x41, 0x18, 0x12, 0x16, 0x4c, 0x69, 0x73,
0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0xc3, 0x01, 0x0a, 0x10, 0x43,
0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12,
0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76,
0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x92, 0x41, 0x1e, 0x12,
0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x1d, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x12, 0xc1, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x42, 0x92, 0x41, 0x17, 0x12, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61,
0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x22, 0x32, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x12, 0xb4, 0x01, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34,
0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x92, 0x41, 0x16, 0x12, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x73, 0x20, 0x61, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x82,
0xd3, 0xe4, 0x93, 0x02, 0x16, 0x2a, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, 0x5b, 0x5a, 0x4b, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0xa2, 0xe3, 0x29, 0x0a, 0x63, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_controller_api_services_v1_credential_service_proto_rawDescOnce sync.Once
file_controller_api_services_v1_credential_service_proto_rawDescData = file_controller_api_services_v1_credential_service_proto_rawDesc
)
func file_controller_api_services_v1_credential_service_proto_rawDescGZIP() []byte {
file_controller_api_services_v1_credential_service_proto_rawDescOnce.Do(func() {
file_controller_api_services_v1_credential_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_services_v1_credential_service_proto_rawDescData)
})
return file_controller_api_services_v1_credential_service_proto_rawDescData
}
var file_controller_api_services_v1_credential_service_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_controller_api_services_v1_credential_service_proto_goTypes = []interface{}{
(*GetCredentialRequest)(nil), // 0: controller.api.services.v1.GetCredentialRequest
(*GetCredentialResponse)(nil), // 1: controller.api.services.v1.GetCredentialResponse
(*ListCredentialsRequest)(nil), // 2: controller.api.services.v1.ListCredentialsRequest
(*ListCredentialsResponse)(nil), // 3: controller.api.services.v1.ListCredentialsResponse
(*CreateCredentialRequest)(nil), // 4: controller.api.services.v1.CreateCredentialRequest
(*CreateCredentialResponse)(nil), // 5: controller.api.services.v1.CreateCredentialResponse
(*UpdateCredentialRequest)(nil), // 6: controller.api.services.v1.UpdateCredentialRequest
(*UpdateCredentialResponse)(nil), // 7: controller.api.services.v1.UpdateCredentialResponse
(*DeleteCredentialRequest)(nil), // 8: controller.api.services.v1.DeleteCredentialRequest
(*DeleteCredentialResponse)(nil), // 9: controller.api.services.v1.DeleteCredentialResponse
(*credentials.Credential)(nil), // 10: controller.api.resources.credentials.v1.Credential
(*fieldmaskpb.FieldMask)(nil), // 11: google.protobuf.FieldMask
}
var file_controller_api_services_v1_credential_service_proto_depIdxs = []int32{
10, // 0: controller.api.services.v1.GetCredentialResponse.item:type_name -> controller.api.resources.credentials.v1.Credential
10, // 1: controller.api.services.v1.ListCredentialsResponse.items:type_name -> controller.api.resources.credentials.v1.Credential
10, // 2: controller.api.services.v1.CreateCredentialRequest.item:type_name -> controller.api.resources.credentials.v1.Credential
10, // 3: controller.api.services.v1.CreateCredentialResponse.item:type_name -> controller.api.resources.credentials.v1.Credential
10, // 4: controller.api.services.v1.UpdateCredentialRequest.item:type_name -> controller.api.resources.credentials.v1.Credential
11, // 5: controller.api.services.v1.UpdateCredentialRequest.update_mask:type_name -> google.protobuf.FieldMask
10, // 6: controller.api.services.v1.UpdateCredentialResponse.item:type_name -> controller.api.resources.credentials.v1.Credential
0, // 7: controller.api.services.v1.CredentialService.GetCredential:input_type -> controller.api.services.v1.GetCredentialRequest
2, // 8: controller.api.services.v1.CredentialService.ListCredentials:input_type -> controller.api.services.v1.ListCredentialsRequest
4, // 9: controller.api.services.v1.CredentialService.CreateCredential:input_type -> controller.api.services.v1.CreateCredentialRequest
6, // 10: controller.api.services.v1.CredentialService.UpdateCredential:input_type -> controller.api.services.v1.UpdateCredentialRequest
8, // 11: controller.api.services.v1.CredentialService.DeleteCredential:input_type -> controller.api.services.v1.DeleteCredentialRequest
1, // 12: controller.api.services.v1.CredentialService.GetCredential:output_type -> controller.api.services.v1.GetCredentialResponse
3, // 13: controller.api.services.v1.CredentialService.ListCredentials:output_type -> controller.api.services.v1.ListCredentialsResponse
5, // 14: controller.api.services.v1.CredentialService.CreateCredential:output_type -> controller.api.services.v1.CreateCredentialResponse
7, // 15: controller.api.services.v1.CredentialService.UpdateCredential:output_type -> controller.api.services.v1.UpdateCredentialResponse
9, // 16: controller.api.services.v1.CredentialService.DeleteCredential:output_type -> controller.api.services.v1.DeleteCredentialResponse
12, // [12:17] is the sub-list for method output_type
7, // [7:12] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension type_name
7, // [7:7] is the sub-list for extension extendee
0, // [0:7] is the sub-list for field type_name
}
func init() { file_controller_api_services_v1_credential_service_proto_init() }
func file_controller_api_services_v1_credential_service_proto_init() {
if File_controller_api_services_v1_credential_service_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_controller_api_services_v1_credential_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetCredentialRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_credential_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetCredentialResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_credential_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListCredentialsRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_credential_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListCredentialsResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_credential_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateCredentialRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_credential_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateCredentialResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_credential_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UpdateCredentialRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_credential_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UpdateCredentialResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_credential_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeleteCredentialRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_credential_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeleteCredentialResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_api_services_v1_credential_service_proto_rawDesc,
NumEnums: 0,
NumMessages: 10,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_controller_api_services_v1_credential_service_proto_goTypes,
DependencyIndexes: file_controller_api_services_v1_credential_service_proto_depIdxs,
MessageInfos: file_controller_api_services_v1_credential_service_proto_msgTypes,
}.Build()
File_controller_api_services_v1_credential_service_proto = out.File
file_controller_api_services_v1_credential_service_proto_rawDesc = nil
file_controller_api_services_v1_credential_service_proto_goTypes = nil
file_controller_api_services_v1_credential_service_proto_depIdxs = nil
}
@@ -0,0 +1,632 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: controller/api/services/v1/credential_service.proto
/*
Package services is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package services
import (
"context"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = metadata.Join
func request_CredentialService_GetCredential_0(ctx context.Context, marshaler runtime.Marshaler, client CredentialServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetCredentialRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := client.GetCredential(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_CredentialService_GetCredential_0(ctx context.Context, marshaler runtime.Marshaler, server CredentialServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetCredentialRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := server.GetCredential(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_CredentialService_ListCredentials_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_CredentialService_ListCredentials_0(ctx context.Context, marshaler runtime.Marshaler, client CredentialServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ListCredentialsRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_CredentialService_ListCredentials_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ListCredentials(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_CredentialService_ListCredentials_0(ctx context.Context, marshaler runtime.Marshaler, server CredentialServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ListCredentialsRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_CredentialService_ListCredentials_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ListCredentials(ctx, &protoReq)
return msg, metadata, err
}
func request_CredentialService_CreateCredential_0(ctx context.Context, marshaler runtime.Marshaler, client CredentialServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq CreateCredentialRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.CreateCredential(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_CredentialService_CreateCredential_0(ctx context.Context, marshaler runtime.Marshaler, server CredentialServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq CreateCredentialRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.CreateCredential(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_CredentialService_UpdateCredential_0 = &utilities.DoubleArray{Encoding: map[string]int{"item": 0, "id": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}}
)
func request_CredentialService_UpdateCredential_0(ctx context.Context, marshaler runtime.Marshaler, client CredentialServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdateCredentialRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 {
if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Item); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} else {
protoReq.UpdateMask = fieldMask
}
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_CredentialService_UpdateCredential_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.UpdateCredential(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_CredentialService_UpdateCredential_0(ctx context.Context, marshaler runtime.Marshaler, server CredentialServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdateCredentialRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 {
if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Item); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} else {
protoReq.UpdateMask = fieldMask
}
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_CredentialService_UpdateCredential_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UpdateCredential(ctx, &protoReq)
return msg, metadata, err
}
func request_CredentialService_DeleteCredential_0(ctx context.Context, marshaler runtime.Marshaler, client CredentialServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq DeleteCredentialRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := client.DeleteCredential(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_CredentialService_DeleteCredential_0(ctx context.Context, marshaler runtime.Marshaler, server CredentialServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq DeleteCredentialRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := server.DeleteCredential(ctx, &protoReq)
return msg, metadata, err
}
// RegisterCredentialServiceHandlerServer registers the http handlers for service CredentialService to "mux".
// UnaryRPC :call CredentialServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterCredentialServiceHandlerFromEndpoint instead.
func RegisterCredentialServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server CredentialServiceServer) error {
mux.Handle("GET", pattern_CredentialService_GetCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/GetCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_CredentialService_GetCredential_0(ctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CredentialService_GetCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_GetCredential_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_CredentialService_ListCredentials_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/ListCredentials", runtime.WithHTTPPathPattern("/v1/credentials"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_CredentialService_ListCredentials_0(ctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CredentialService_ListCredentials_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_CredentialService_CreateCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/CreateCredential", runtime.WithHTTPPathPattern("/v1/credentials"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_CredentialService_CreateCredential_0(ctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CredentialService_CreateCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_CreateCredential_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("PATCH", pattern_CredentialService_UpdateCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/UpdateCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_CredentialService_UpdateCredential_0(ctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CredentialService_UpdateCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_UpdateCredential_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_CredentialService_DeleteCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/DeleteCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_CredentialService_DeleteCredential_0(ctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CredentialService_DeleteCredential_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterCredentialServiceHandlerFromEndpoint is same as RegisterCredentialServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterCredentialServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterCredentialServiceHandler(ctx, mux, conn)
}
// RegisterCredentialServiceHandler registers the http handlers for service CredentialService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterCredentialServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterCredentialServiceHandlerClient(ctx, mux, NewCredentialServiceClient(conn))
}
// RegisterCredentialServiceHandlerClient registers the http handlers for service CredentialService
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "CredentialServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "CredentialServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "CredentialServiceClient" to call the correct interceptors.
func RegisterCredentialServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client CredentialServiceClient) error {
mux.Handle("GET", pattern_CredentialService_GetCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/GetCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_CredentialService_GetCredential_0(ctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CredentialService_GetCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_GetCredential_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_CredentialService_ListCredentials_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/ListCredentials", runtime.WithHTTPPathPattern("/v1/credentials"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_CredentialService_ListCredentials_0(ctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CredentialService_ListCredentials_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_CredentialService_CreateCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/CreateCredential", runtime.WithHTTPPathPattern("/v1/credentials"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_CredentialService_CreateCredential_0(ctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CredentialService_CreateCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_CreateCredential_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("PATCH", pattern_CredentialService_UpdateCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/UpdateCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_CredentialService_UpdateCredential_0(ctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CredentialService_UpdateCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_UpdateCredential_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_CredentialService_DeleteCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/DeleteCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_CredentialService_DeleteCredential_0(ctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CredentialService_DeleteCredential_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
type response_CredentialService_GetCredential_0 struct {
proto.Message
}
func (m response_CredentialService_GetCredential_0) XXX_ResponseBody() interface{} {
response := m.Message.(*GetCredentialResponse)
return response.Item
}
type response_CredentialService_CreateCredential_0 struct {
proto.Message
}
func (m response_CredentialService_CreateCredential_0) XXX_ResponseBody() interface{} {
response := m.Message.(*CreateCredentialResponse)
return response.Item
}
type response_CredentialService_UpdateCredential_0 struct {
proto.Message
}
func (m response_CredentialService_UpdateCredential_0) XXX_ResponseBody() interface{} {
response := m.Message.(*UpdateCredentialResponse)
return response.Item
}
var (
pattern_CredentialService_GetCredential_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "credentials", "id"}, ""))
pattern_CredentialService_ListCredentials_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "credentials"}, ""))
pattern_CredentialService_CreateCredential_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "credentials"}, ""))
pattern_CredentialService_UpdateCredential_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "credentials", "id"}, ""))
pattern_CredentialService_DeleteCredential_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "credentials", "id"}, ""))
)
var (
forward_CredentialService_GetCredential_0 = runtime.ForwardResponseMessage
forward_CredentialService_ListCredentials_0 = runtime.ForwardResponseMessage
forward_CredentialService_CreateCredential_0 = runtime.ForwardResponseMessage
forward_CredentialService_UpdateCredential_0 = runtime.ForwardResponseMessage
forward_CredentialService_DeleteCredential_0 = runtime.ForwardResponseMessage
)
@@ -0,0 +1,287 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package services
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// CredentialServiceClient is the client API for CredentialService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type CredentialServiceClient interface {
// GetCredential returns a stored Credential if present. The provided request
// must include the Credential Store id. If missing, malformed or referencing a
// non existing resource an error is returned.
GetCredential(ctx context.Context, in *GetCredentialRequest, opts ...grpc.CallOption) (*GetCredentialResponse, error)
// ListCredentials returns a list of stored Credentials which are in the
// provided store. The request must include the Credential Store id and if missing,
// malformed, or referencing a non existing scope, an error is returned.
ListCredentials(ctx context.Context, in *ListCredentialsRequest, opts ...grpc.CallOption) (*ListCredentialsResponse, error)
// CreateCredential creates and stores an Credential in boundary. The
// provided request must include the credential_store_id in which the Credential
// will be created. If the store id is missing, malformed or referencing a
// non existing resource an error is returned. If a name is provided that is
// in use in another Credential in the same store, an error is returned.
CreateCredential(ctx context.Context, in *CreateCredentialRequest, opts ...grpc.CallOption) (*CreateCredentialResponse, error)
// UpdateCredential updates an existing Credential in boundary. The provided
// Credential must not have any read only fields set. The update mask must be
// included in the request and contain at least 1 mutable field. To unset
// a field's value, include the field in the update mask and don't set it
// in the provided user. An error is returned if the Credential id is missing
// or reference a non existing resource. An error is also returned if the
// request attempts to update the name to one that is already in use by
// another Credential in the parent scope.
UpdateCredential(ctx context.Context, in *UpdateCredentialRequest, opts ...grpc.CallOption) (*UpdateCredentialResponse, error)
// DeleteCredential removes an Credential from Boundary. If the Credential id
// is malformed or not provided an error is returned.
DeleteCredential(ctx context.Context, in *DeleteCredentialRequest, opts ...grpc.CallOption) (*DeleteCredentialResponse, error)
}
type credentialServiceClient struct {
cc grpc.ClientConnInterface
}
func NewCredentialServiceClient(cc grpc.ClientConnInterface) CredentialServiceClient {
return &credentialServiceClient{cc}
}
func (c *credentialServiceClient) GetCredential(ctx context.Context, in *GetCredentialRequest, opts ...grpc.CallOption) (*GetCredentialResponse, error) {
out := new(GetCredentialResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.CredentialService/GetCredential", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *credentialServiceClient) ListCredentials(ctx context.Context, in *ListCredentialsRequest, opts ...grpc.CallOption) (*ListCredentialsResponse, error) {
out := new(ListCredentialsResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.CredentialService/ListCredentials", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *credentialServiceClient) CreateCredential(ctx context.Context, in *CreateCredentialRequest, opts ...grpc.CallOption) (*CreateCredentialResponse, error) {
out := new(CreateCredentialResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.CredentialService/CreateCredential", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *credentialServiceClient) UpdateCredential(ctx context.Context, in *UpdateCredentialRequest, opts ...grpc.CallOption) (*UpdateCredentialResponse, error) {
out := new(UpdateCredentialResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.CredentialService/UpdateCredential", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *credentialServiceClient) DeleteCredential(ctx context.Context, in *DeleteCredentialRequest, opts ...grpc.CallOption) (*DeleteCredentialResponse, error) {
out := new(DeleteCredentialResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.CredentialService/DeleteCredential", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// CredentialServiceServer is the server API for CredentialService service.
// All implementations must embed UnimplementedCredentialServiceServer
// for forward compatibility
type CredentialServiceServer interface {
// GetCredential returns a stored Credential if present. The provided request
// must include the Credential Store id. If missing, malformed or referencing a
// non existing resource an error is returned.
GetCredential(context.Context, *GetCredentialRequest) (*GetCredentialResponse, error)
// ListCredentials returns a list of stored Credentials which are in the
// provided store. The request must include the Credential Store id and if missing,
// malformed, or referencing a non existing scope, an error is returned.
ListCredentials(context.Context, *ListCredentialsRequest) (*ListCredentialsResponse, error)
// CreateCredential creates and stores an Credential in boundary. The
// provided request must include the credential_store_id in which the Credential
// will be created. If the store id is missing, malformed or referencing a
// non existing resource an error is returned. If a name is provided that is
// in use in another Credential in the same store, an error is returned.
CreateCredential(context.Context, *CreateCredentialRequest) (*CreateCredentialResponse, error)
// UpdateCredential updates an existing Credential in boundary. The provided
// Credential must not have any read only fields set. The update mask must be
// included in the request and contain at least 1 mutable field. To unset
// a field's value, include the field in the update mask and don't set it
// in the provided user. An error is returned if the Credential id is missing
// or reference a non existing resource. An error is also returned if the
// request attempts to update the name to one that is already in use by
// another Credential in the parent scope.
UpdateCredential(context.Context, *UpdateCredentialRequest) (*UpdateCredentialResponse, error)
// DeleteCredential removes an Credential from Boundary. If the Credential id
// is malformed or not provided an error is returned.
DeleteCredential(context.Context, *DeleteCredentialRequest) (*DeleteCredentialResponse, error)
mustEmbedUnimplementedCredentialServiceServer()
}
// UnimplementedCredentialServiceServer must be embedded to have forward compatible implementations.
type UnimplementedCredentialServiceServer struct {
}
func (UnimplementedCredentialServiceServer) GetCredential(context.Context, *GetCredentialRequest) (*GetCredentialResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCredential not implemented")
}
func (UnimplementedCredentialServiceServer) ListCredentials(context.Context, *ListCredentialsRequest) (*ListCredentialsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListCredentials not implemented")
}
func (UnimplementedCredentialServiceServer) CreateCredential(context.Context, *CreateCredentialRequest) (*CreateCredentialResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateCredential not implemented")
}
func (UnimplementedCredentialServiceServer) UpdateCredential(context.Context, *UpdateCredentialRequest) (*UpdateCredentialResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateCredential not implemented")
}
func (UnimplementedCredentialServiceServer) DeleteCredential(context.Context, *DeleteCredentialRequest) (*DeleteCredentialResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteCredential not implemented")
}
func (UnimplementedCredentialServiceServer) mustEmbedUnimplementedCredentialServiceServer() {}
// UnsafeCredentialServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to CredentialServiceServer will
// result in compilation errors.
type UnsafeCredentialServiceServer interface {
mustEmbedUnimplementedCredentialServiceServer()
}
func RegisterCredentialServiceServer(s grpc.ServiceRegistrar, srv CredentialServiceServer) {
s.RegisterService(&CredentialService_ServiceDesc, srv)
}
func _CredentialService_GetCredential_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetCredentialRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CredentialServiceServer).GetCredential(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.CredentialService/GetCredential",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CredentialServiceServer).GetCredential(ctx, req.(*GetCredentialRequest))
}
return interceptor(ctx, in, info, handler)
}
func _CredentialService_ListCredentials_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListCredentialsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CredentialServiceServer).ListCredentials(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.CredentialService/ListCredentials",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CredentialServiceServer).ListCredentials(ctx, req.(*ListCredentialsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _CredentialService_CreateCredential_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateCredentialRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CredentialServiceServer).CreateCredential(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.CredentialService/CreateCredential",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CredentialServiceServer).CreateCredential(ctx, req.(*CreateCredentialRequest))
}
return interceptor(ctx, in, info, handler)
}
func _CredentialService_UpdateCredential_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateCredentialRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CredentialServiceServer).UpdateCredential(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.CredentialService/UpdateCredential",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CredentialServiceServer).UpdateCredential(ctx, req.(*UpdateCredentialRequest))
}
return interceptor(ctx, in, info, handler)
}
func _CredentialService_DeleteCredential_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteCredentialRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CredentialServiceServer).DeleteCredential(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.CredentialService/DeleteCredential",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CredentialServiceServer).DeleteCredential(ctx, req.(*DeleteCredentialRequest))
}
return interceptor(ctx, in, info, handler)
}
// CredentialService_ServiceDesc is the grpc.ServiceDesc for CredentialService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var CredentialService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "controller.api.services.v1.CredentialService",
HandlerType: (*CredentialServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetCredential",
Handler: _CredentialService_GetCredential_Handler,
},
{
MethodName: "ListCredentials",
Handler: _CredentialService_ListCredentials_Handler,
},
{
MethodName: "CreateCredential",
Handler: _CredentialService_CreateCredential_Handler,
},
{
MethodName: "UpdateCredential",
Handler: _CredentialService_UpdateCredential_Handler,
},
{
MethodName: "DeleteCredential",
Handler: _CredentialService_DeleteCredential_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "controller/api/services/v1/credential_service.proto",
}
File diff suppressed because it is too large. Load diff
@@ -781,210 +781,6 @@ func local_request_TargetService_RemoveTargetHostSources_0(ctx context.Context,
}
func request_TargetService_AddTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, client TargetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AddTargetCredentialLibrariesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := client.AddTargetCredentialLibraries(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_TargetService_AddTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, server TargetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AddTargetCredentialLibrariesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := server.AddTargetCredentialLibraries(ctx, &protoReq)
return msg, metadata, err
}
func request_TargetService_SetTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, client TargetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SetTargetCredentialLibrariesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := client.SetTargetCredentialLibraries(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_TargetService_SetTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, server TargetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SetTargetCredentialLibrariesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := server.SetTargetCredentialLibraries(ctx, &protoReq)
return msg, metadata, err
}
func request_TargetService_RemoveTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, client TargetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq RemoveTargetCredentialLibrariesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := client.RemoveTargetCredentialLibraries(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_TargetService_RemoveTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, server TargetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq RemoveTargetCredentialLibrariesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := server.RemoveTargetCredentialLibraries(ctx, &protoReq)
return msg, metadata, err
}
func request_TargetService_AddTargetCredentialSources_0(ctx context.Context, marshaler runtime.Marshaler, client TargetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AddTargetCredentialSourcesRequest
var metadata runtime.ServerMetadata
@@ -1483,78 +1279,6 @@ func RegisterTargetServiceHandlerServer(ctx context.Context, mux *runtime.ServeM
})
mux.Handle("POST", pattern_TargetService_AddTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.TargetService/AddTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:add-credential-libraries"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_TargetService_AddTargetCredentialLibraries_0(ctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_TargetService_AddTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_AddTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_TargetService_SetTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.TargetService/SetTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:set-credential-libraries"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_TargetService_SetTargetCredentialLibraries_0(ctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_TargetService_SetTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_SetTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_TargetService_RemoveTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.TargetService/RemoveTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:remove-credential-libraries"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_TargetService_RemoveTargetCredentialLibraries_0(ctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_TargetService_RemoveTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_RemoveTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_TargetService_AddTargetCredentialSources_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -1920,69 +1644,6 @@ func RegisterTargetServiceHandlerClient(ctx context.Context, mux *runtime.ServeM
})
mux.Handle("POST", pattern_TargetService_AddTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.TargetService/AddTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:add-credential-libraries"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_TargetService_AddTargetCredentialLibraries_0(ctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_TargetService_AddTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_AddTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_TargetService_SetTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.TargetService/SetTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:set-credential-libraries"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_TargetService_SetTargetCredentialLibraries_0(ctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_TargetService_SetTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_SetTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_TargetService_RemoveTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.TargetService/RemoveTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:remove-credential-libraries"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_TargetService_RemoveTargetCredentialLibraries_0(ctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_TargetService_RemoveTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_RemoveTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_TargetService_AddTargetCredentialSources_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -2139,33 +1800,6 @@ func (m response_TargetService_RemoveTargetHostSources_0) XXX_ResponseBody() int
return response.Item
}
type response_TargetService_AddTargetCredentialLibraries_0 struct {
proto.Message
}
func (m response_TargetService_AddTargetCredentialLibraries_0) XXX_ResponseBody() interface{} {
response := m.Message.(*AddTargetCredentialLibrariesResponse)
return response.Item
}
type response_TargetService_SetTargetCredentialLibraries_0 struct {
proto.Message
}
func (m response_TargetService_SetTargetCredentialLibraries_0) XXX_ResponseBody() interface{} {
response := m.Message.(*SetTargetCredentialLibrariesResponse)
return response.Item
}
type response_TargetService_RemoveTargetCredentialLibraries_0 struct {
proto.Message
}
func (m response_TargetService_RemoveTargetCredentialLibraries_0) XXX_ResponseBody() interface{} {
response := m.Message.(*RemoveTargetCredentialLibrariesResponse)
return response.Item
}
type response_TargetService_AddTargetCredentialSources_0 struct {
proto.Message
}
@@ -2218,12 +1852,6 @@ var (
pattern_TargetService_RemoveTargetHostSources_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "remove-host-sources"))
pattern_TargetService_AddTargetCredentialLibraries_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "add-credential-libraries"))
pattern_TargetService_SetTargetCredentialLibraries_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "set-credential-libraries"))
pattern_TargetService_RemoveTargetCredentialLibraries_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "remove-credential-libraries"))
pattern_TargetService_AddTargetCredentialSources_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "add-credential-sources"))
pattern_TargetService_SetTargetCredentialSources_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "set-credential-sources"))
@@ -2256,12 +1884,6 @@ var (
forward_TargetService_RemoveTargetHostSources_0 = runtime.ForwardResponseMessage
forward_TargetService_AddTargetCredentialLibraries_0 = runtime.ForwardResponseMessage
forward_TargetService_SetTargetCredentialLibraries_0 = runtime.ForwardResponseMessage
forward_TargetService_RemoveTargetCredentialLibraries_0 = runtime.ForwardResponseMessage
forward_TargetService_AddTargetCredentialSources_0 = runtime.ForwardResponseMessage
forward_TargetService_SetTargetCredentialSources_0 = runtime.ForwardResponseMessage
@@ -94,33 +94,6 @@ type TargetServiceClient interface {
// is returned if a Host Source is attempted to be removed from the Target
// when the Target does not have the Host Set.
RemoveTargetHostSources(ctx context.Context, in *RemoveTargetHostSourcesRequest, opts ...grpc.CallOption) (*RemoveTargetHostSourcesResponse, error)
// Deprecated: Do not use.
// AddTargetCredentialLibraries adds Credential Libraries to this Target.
// The provided request must include the Target ID to which the Credential
// Libraries will be added. All Credential Libraries added to the provided
// Target must be a child of a Store that is in the same scope as this
// Target. If the scope or Target IDs are missing, malformed, or reference
// non-existing resources, an error is returned. An error is returned if a
// Credential Library is attempted to be added to a target that is already
// present on the Target.
AddTargetCredentialLibraries(ctx context.Context, in *AddTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*AddTargetCredentialLibrariesResponse, error)
// Deprecated: Do not use.
// SetTargetCredentialLibraries sets the Target's Credential Libraries.
// Any existing Credential Libraries on the Target are deleted if they are
// not included in this request. The provided request must include the scope,
// and the Target ID on which the Credential Libraries will be set. All
// Credential Libraries in the request must be a child of a Store that is
// in the same scope as the provided Target. If any IDs are missing,
// malformed, or references a non-existing resource, an error is returned.
SetTargetCredentialLibraries(ctx context.Context, in *SetTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*SetTargetCredentialLibrariesResponse, error)
// Deprecated: Do not use.
// RemoveTargetCredentialLibraries removes the Credential Libraries from the
// specified Target. The provided request must include the Target ID for the
// Target from which the Credential Libraries will be removed. If the ID is
// missing, or malformed, an error is returned. An error is returned if a
// Credential Library is attempted to be removed from the Target when the
// Target does not have the Credential Library.
RemoveTargetCredentialLibraries(ctx context.Context, in *RemoveTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*RemoveTargetCredentialLibrariesResponse, error)
// AddTargetCredentialSources adds Credential Sources to this Target.
// The provided request must include the Target ID to which the Credential
// Sources will be added. All Credential Sources added to the provided
@@ -263,36 +236,6 @@ func (c *targetServiceClient) RemoveTargetHostSources(ctx context.Context, in *R
return out, nil
}
// Deprecated: Do not use.
func (c *targetServiceClient) AddTargetCredentialLibraries(ctx context.Context, in *AddTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*AddTargetCredentialLibrariesResponse, error) {
out := new(AddTargetCredentialLibrariesResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.TargetService/AddTargetCredentialLibraries", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Deprecated: Do not use.
func (c *targetServiceClient) SetTargetCredentialLibraries(ctx context.Context, in *SetTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*SetTargetCredentialLibrariesResponse, error) {
out := new(SetTargetCredentialLibrariesResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.TargetService/SetTargetCredentialLibraries", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Deprecated: Do not use.
func (c *targetServiceClient) RemoveTargetCredentialLibraries(ctx context.Context, in *RemoveTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*RemoveTargetCredentialLibrariesResponse, error) {
out := new(RemoveTargetCredentialLibrariesResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.TargetService/RemoveTargetCredentialLibraries", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *targetServiceClient) AddTargetCredentialSources(ctx context.Context, in *AddTargetCredentialSourcesRequest, opts ...grpc.CallOption) (*AddTargetCredentialSourcesResponse, error) {
out := new(AddTargetCredentialSourcesResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.TargetService/AddTargetCredentialSources", in, out, opts...)
@@ -400,33 +343,6 @@ type TargetServiceServer interface {
// is returned if a Host Source is attempted to be removed from the Target
// when the Target does not have the Host Set.
RemoveTargetHostSources(context.Context, *RemoveTargetHostSourcesRequest) (*RemoveTargetHostSourcesResponse, error)
// Deprecated: Do not use.
// AddTargetCredentialLibraries adds Credential Libraries to this Target.
// The provided request must include the Target ID to which the Credential
// Libraries will be added. All Credential Libraries added to the provided
// Target must be a child of a Store that is in the same scope as this
// Target. If the scope or Target IDs are missing, malformed, or reference
// non-existing resources, an error is returned. An error is returned if a
// Credential Library is attempted to be added to a target that is already
// present on the Target.
AddTargetCredentialLibraries(context.Context, *AddTargetCredentialLibrariesRequest) (*AddTargetCredentialLibrariesResponse, error)
// Deprecated: Do not use.
// SetTargetCredentialLibraries sets the Target's Credential Libraries.
// Any existing Credential Libraries on the Target are deleted if they are
// not included in this request. The provided request must include the scope,
// and the Target ID on which the Credential Libraries will be set. All
// Credential Libraries in the request must be a child of a Store that is
// in the same scope as the provided Target. If any IDs are missing,
// malformed, or references a non-existing resource, an error is returned.
SetTargetCredentialLibraries(context.Context, *SetTargetCredentialLibrariesRequest) (*SetTargetCredentialLibrariesResponse, error)
// Deprecated: Do not use.
// RemoveTargetCredentialLibraries removes the Credential Libraries from the
// specified Target. The provided request must include the Target ID for the
// Target from which the Credential Libraries will be removed. If the ID is
// missing, or malformed, an error is returned. An error is returned if a
// Credential Library is attempted to be removed from the Target when the
// Target does not have the Credential Library.
RemoveTargetCredentialLibraries(context.Context, *RemoveTargetCredentialLibrariesRequest) (*RemoveTargetCredentialLibrariesResponse, error)
// AddTargetCredentialSources adds Credential Sources to this Target.
// The provided request must include the Target ID to which the Credential
// Sources will be added. All Credential Sources added to the provided
@@ -494,15 +410,6 @@ func (UnimplementedTargetServiceServer) SetTargetHostSources(context.Context, *S
func (UnimplementedTargetServiceServer) RemoveTargetHostSources(context.Context, *RemoveTargetHostSourcesRequest) (*RemoveTargetHostSourcesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveTargetHostSources not implemented")
}
func (UnimplementedTargetServiceServer) AddTargetCredentialLibraries(context.Context, *AddTargetCredentialLibrariesRequest) (*AddTargetCredentialLibrariesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddTargetCredentialLibraries not implemented")
}
func (UnimplementedTargetServiceServer) SetTargetCredentialLibraries(context.Context, *SetTargetCredentialLibrariesRequest) (*SetTargetCredentialLibrariesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetTargetCredentialLibraries not implemented")
}
func (UnimplementedTargetServiceServer) RemoveTargetCredentialLibraries(context.Context, *RemoveTargetCredentialLibrariesRequest) (*RemoveTargetCredentialLibrariesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveTargetCredentialLibraries not implemented")
}
func (UnimplementedTargetServiceServer) AddTargetCredentialSources(context.Context, *AddTargetCredentialSourcesRequest) (*AddTargetCredentialSourcesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddTargetCredentialSources not implemented")
}
@@ -741,60 +648,6 @@ func _TargetService_RemoveTargetHostSources_Handler(srv interface{}, ctx context
return interceptor(ctx, in, info, handler)
}
func _TargetService_AddTargetCredentialLibraries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddTargetCredentialLibrariesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TargetServiceServer).AddTargetCredentialLibraries(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.TargetService/AddTargetCredentialLibraries",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TargetServiceServer).AddTargetCredentialLibraries(ctx, req.(*AddTargetCredentialLibrariesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _TargetService_SetTargetCredentialLibraries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetTargetCredentialLibrariesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TargetServiceServer).SetTargetCredentialLibraries(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.TargetService/SetTargetCredentialLibraries",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TargetServiceServer).SetTargetCredentialLibraries(ctx, req.(*SetTargetCredentialLibrariesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _TargetService_RemoveTargetCredentialLibraries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveTargetCredentialLibrariesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TargetServiceServer).RemoveTargetCredentialLibraries(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.TargetService/RemoveTargetCredentialLibraries",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TargetServiceServer).RemoveTargetCredentialLibraries(ctx, req.(*RemoveTargetCredentialLibrariesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _TargetService_AddTargetCredentialSources_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddTargetCredentialSourcesRequest)
if err := dec(in); err != nil {
@@ -904,18 +757,6 @@ var TargetService_ServiceDesc = grpc.ServiceDesc{
MethodName: "RemoveTargetHostSources",
Handler: _TargetService_RemoveTargetHostSources_Handler,
},
{
MethodName: "AddTargetCredentialLibraries",
Handler: _TargetService_AddTargetCredentialLibraries_Handler,
},
{
MethodName: "SetTargetCredentialLibraries",
Handler: _TargetService_SetTargetCredentialLibraries_Handler,
},
{
MethodName: "RemoveTargetCredentialLibraries",
Handler: _TargetService_RemoveTargetCredentialLibraries_Handler,
},
{
MethodName: "AddTargetCredentialSources",
Handler: _TargetService_AddTargetCredentialSources_Handler,
+1 -1
View File
@@ -108,7 +108,7 @@ func Test_ActionParsingValidation(t *testing.T) {
func Test_ValidateType(t *testing.T) {
t.Parallel()
var g Grant
for i := resource.Unknown; i <= resource.CredentialLibrary; i++ {
for i := resource.Unknown; i <= resource.Credential; i++ {
g.typ = i
if i == resource.Controller {
assert.Error(t, g.validateType())
@@ -0,0 +1,102 @@
syntax = "proto3";
package controller.api.resources.credentials.v1;
import "controller/api/resources/scopes/v1/scope.proto";
import "controller/custom_options/v1/options.proto";
import "google/api/visibility.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
option go_package = "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials;credentials";
// Credential contains all fields related to an Credential resource
message Credential {
// Output only. The ID of the Credential.
string id = 10; // @gotags: `class:"public"`
// The ID of the Credential Store of which this Credential is a part.
string credential_store_id = 20 [json_name = "credential_store_id"]; // @gotags: `class:"public"`
// Output only. Scope information for this Credential.
resources.scopes.v1.ScopeInfo scope = 30;
// Optional name for identification purposes.
google.protobuf.StringValue name = 40 [
(custom_options.v1.generate_sdk_option) = true,
(custom_options.v1.mask_mapping) = {
this: "name"
that: "Name"
}
]; // @gotags: `class:"public"`
// Optional user-set description for identification purposes.
google.protobuf.StringValue description = 50 [
(custom_options.v1.generate_sdk_option) = true,
(custom_options.v1.mask_mapping) = {
this: "description"
that: "Description"
}
]; // @gotags: `class:"public"`
// Output only. The time this resource was created.
google.protobuf.Timestamp created_time = 60 [json_name = "created_time"]; // @gotags: `class:"public"`
// Output only. The time this resource was last updated.
google.protobuf.Timestamp updated_time = 70 [json_name = "updated_time"]; // @gotags: `class:"public"`
// Version is used in mutation requests, after the initial creation, to ensure this resource has not changed.
// The mutation will fail if the version does not match the latest known good version.
uint32 version = 80; // @gotags: `class:"public"`
// The Credential type.
string type = 90; // @gotags: `class:"public"`
oneof attrs {
// The attributes that are applicable for the specific Credential type.
google.protobuf.Struct attributes = 100 [
(custom_options.v1.generate_sdk_option) = true,
(custom_options.v1.subtype) = "default"
];
UsernamePasswordAttributes username_password_attributes = 101 [
(google.api.field_visibility).restriction = "INTERNAL",
(custom_options.v1.generate_sdk_option) = true,
(custom_options.v1.subtype) = "username_password"
];
}
// Output only. The available actions on this resource for this user.
repeated string authorized_actions = 300 [json_name = "authorized_actions"]; // @gotags: `class:"public"`
}
// The attributes of a UsernamePassword Credential.
message UsernamePasswordAttributes {
// The username associated with the credential.
google.protobuf.StringValue username = 10 [
(custom_options.v1.generate_sdk_option) = true,
(custom_options.v1.mask_mapping) = {
this: "attributes.username"
that: "Username"
}
]; // @gotags: `class:"public"`
// Input only. The password associated with the credential.
google.protobuf.StringValue password = 20 [
json_name = "password",
(custom_options.v1.generate_sdk_option) = true,
(custom_options.v1.mask_mapping) = {
this: "attributes.password"
that: "Password"
}
]; // @gotags: `class:"secret"`
// Output only. The hmac value of the password.
string password_hmac = 30 [
json_name = "password_hmac",
(custom_options.v1.mask_mapping) = {
this: "attributes.password_hmac"
that: "PasswordHmac"
}
]; // @gotags: `class:"public"`
}
@@ -47,23 +47,6 @@ message CredentialSource {
string credential_type = 70; // @gotags: `class:"public"`
}
message CredentialLibrary {
// The ID of the Credential Library.
string id = 10; // @gotags: `class:"public"`
// Output only. The name of the Credential Library.
string name = 20; // @gotags: `class:"public"`
// Output only. The description of the Credential Library.
string description = 30; // @gotags: `class:"public"`
// Output only. The Credential Store to which this Credential Library belongs.
string credential_store_id = 40 [json_name = "credential_store_id"]; // @gotags: `class:"public"`
// Output only. The type of the credential library.
string type = 60; // @gotags: `class:"public"`
}
// The actual secret for a session credential.
message SessionSecret {
// Output only. The base64-encoded value representing the raw bytes from the
@@ -79,15 +62,16 @@ message SessionCredential {
// Output only. The credential source information.
CredentialSource credential_source = 1;
// Output only. The library which generated this credential. Deprecated: use credential_source instead.
CredentialLibrary credential_library = 10 [deprecated = true];
// Output only. The secret of this credential base64 encoded.
SessionSecret secret = 20;
// Output only. The fields of the strongly typed credential, empty if the credential type of the
// credential source is unspecified.
google.protobuf.Struct credential = 40;
// Deprecated fields
reserved "credential_library";
reserved 10;
}
// Target contains all fields related to a Target resource
@@ -174,17 +158,6 @@ message Target {
}
]; // @gotags: `class:"public"`
// Output only. The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead.
repeated string application_credential_library_ids = 150 [
json_name = "application_credential_library_ids",
deprecated = true
]; // @gotags: `class:"public"`
// Output only. The application credential libraries associated with this Target. Deprecated: use application_credential_sources instead.
repeated CredentialLibrary application_credential_libraries = 180 [
json_name = "application_credential_libraries",
deprecated = true
];
// Output only. The IDs of the application credential source ids associated with this Target.
repeated string application_credential_source_ids = 400 [json_name = "application_credential_source_ids"]; // @gotags: `class:"public"`
// Output only. The application credential sources associated with this Target.
@@ -210,6 +183,10 @@ message Target {
// Output only. The available actions on this resource for this user.
repeated string authorized_actions = 300 [json_name = "authorized_actions"]; // @gotags: `class:"public"`
// Deprecated fields
reserved "application_credential_library_ids", "application_credential_libraries";
reserved 150, 180;
}
// TcpTargetAttributes contains attributes relevant to Targets of type "tcp"
Loaded 100 of 160 files, more files were not shown because too many files have changed in this diff. Show more