feat(credential): Add static credential store and username_password credential

This commit is contained in:
Louis Ruch committed 2022-06-14 13:01:17 -07:00
1 parent b6da1ccac7
commit 58d546cdd4
59 files changed
+10859 -205

No files matched your search

+3
View File
@@ -155,6 +155,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
@@ -184,6 +185,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"`
}
+19 -46
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"
@@ -435,15 +436,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,
createTemplate,
@@ -452,50 +462,13 @@ var inputStructs = []*structInfo{
deleteTemplate,
listTemplate,
},
pluralResourceName: "credential-stores",
parentTypeName: "scope",
typeOnCreate: true,
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,
createTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
listTemplate,
},
pluralResourceName: "credential-libraries",
pluralResourceName: "credentials",
parentTypeName: "credential-store",
versionEnabled: true,
createResponseTypes: true,
typeOnCreate: true,
},
// Host related resources
{
inProto: &hostcatalogs.HostCatalog{},
+61
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"
@@ -410,6 +411,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),
@@ -422,6 +429,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{
@@ -0,0 +1,273 @@
// 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", "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()
}
+42
View File
@@ -200,6 +200,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": {
{
@@ -227,6 +242,33 @@ var inputStructs = map[string][]*cmdInfo{
PrefixAttributeFieldErrorsWithSubactionPrefix: true,
},
},
"credentials": {
{
ResourceType: resource.Credential.String(),
Pkg: "credentials",
StdActions: []string{"read", "delete", "list"},
IsAbstractType: true,
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(),
+8 -2
View File
@@ -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,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,121 @@
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"
)
// 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()))
})
}
}
+1 -1
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
@@ -327,14 +327,14 @@ func TestRepository_IssueCredentials(t *testing.T) {
for _, dc := range got {
switch dc.Library().CredentialType() {
case credential.UsernamePasswordType:
if upc, ok := dc.(credential.UserPassword); ok {
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")
}
}
+14 -12
View File
@@ -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"
@@ -14,16 +15,17 @@ import (
)
type (
AuthTokenRepoFactory = oidc.AuthTokenRepoFactory
VaultCredentialRepoFactory = func() (*vault.Repository, error)
IamRepoFactory func() (*iam.Repository, error)
OidcAuthRepoFactory = oidc.OidcRepoFactory
PasswordAuthRepoFactory func() (*password.Repository, error)
ServersRepoFactory func() (*servers.Repository, error)
StaticRepoFactory func() (*static.Repository, error)
PluginHostRepoFactory func() (*pluginhost.Repository, error)
HostPluginRepoFactory func() (*hostplugin.Repository, error)
SessionRepoFactory func() (*session.Repository, error)
ConnectionRepoFactory func() (*session.ConnectionRepository, error)
TargetRepoFactory func() (*target.Repository, error)
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)
ServersRepoFactory func() (*servers.Repository, error)
StaticRepoFactory func() (*static.Repository, error)
PluginHostRepoFactory func() (*pluginhost.Repository, error)
HostPluginRepoFactory func() (*hostplugin.Repository, error)
SessionRepoFactory func() (*session.Repository, error)
ConnectionRepoFactory func() (*session.ConnectionRepository, error)
TargetRepoFactory func() (*target.Repository, error)
)
+17 -12
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"
@@ -64,18 +65,19 @@ type Controller struct {
apiGrpcGatewayTicket string
// Repo factory methods
AuthTokenRepoFn common.AuthTokenRepoFactory
VaultCredentialRepoFn common.VaultCredentialRepoFactory
IamRepoFn common.IamRepoFactory
OidcRepoFn common.OidcAuthRepoFactory
PasswordAuthRepoFn common.PasswordAuthRepoFactory
ServersRepoFn common.ServersRepoFactory
SessionRepoFn common.SessionRepoFactory
ConnectionRepoFn common.ConnectionRepoFactory
StaticHostRepoFn common.StaticRepoFactory
PluginHostRepoFn common.PluginHostRepoFactory
HostPluginRepoFn common.HostPluginRepoFactory
TargetRepoFn common.TargetRepoFactory
AuthTokenRepoFn common.AuthTokenRepoFactory
VaultCredentialRepoFn common.VaultCredentialRepoFactory
StaticCredentialRepoFn common.StaticCredentialRepoFactory
IamRepoFn common.IamRepoFactory
OidcRepoFn common.OidcAuthRepoFactory
PasswordAuthRepoFn common.PasswordAuthRepoFactory
ServersRepoFn common.ServersRepoFactory
SessionRepoFn common.SessionRepoFactory
ConnectionRepoFn common.ConnectionRepoFactory
StaticHostRepoFn common.StaticRepoFactory
PluginHostRepoFn common.PluginHostRepoFactory
HostPluginRepoFn common.HostPluginRepoFactory
TargetRepoFn common.TargetRepoFactory
scheduler *scheduler.Scheduler
@@ -276,6 +278,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)
}
+12 -1
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"
@@ -209,7 +210,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)
}
@@ -222,6 +223,13 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
}
services.RegisterCredentialLibraryServiceServer(s, cl)
}
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)
@@ -279,6 +287,9 @@ func registerGrpcGatewayEndpoints(ctx context.Context, gwMux *runtime.ServeMux,
if err := services.RegisterCredentialLibraryServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil {
return fmt.Errorf("failed to register credential library 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
}
@@ -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)
})
}
}
+246
View File
@@ -20,6 +20,9 @@
{
"name": "controller.api.services.v1.CredentialLibraryService"
},
{
"name": "controller.api.services.v1.CredentialService"
},
{
"name": "controller.api.services.v1.CredentialStoreService"
},
@@ -908,6 +911,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.",
@@ -3946,6 +4090,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": {
@@ -5524,6 +5729,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": {
@@ -5646,6 +5862,9 @@
"controller.api.services.v1.DeleteCredentialLibraryResponse": {
"type": "object"
},
"controller.api.services.v1.DeleteCredentialResponse": {
"type": "object"
},
"controller.api.services.v1.DeleteCredentialStoreResponse": {
"type": "object"
},
@@ -5708,6 +5927,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": {
@@ -5851,6 +6078,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": {
@@ -6137,6 +6375,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",
}
+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 || i == resource.Worker {
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"`
}
@@ -0,0 +1,127 @@
syntax = "proto3";
package controller.api.services.v1;
import "controller/api/resources/credentials/v1/credential.proto";
import "controller/custom_options/v1/options.proto";
import "google/api/annotations.proto";
import "google/protobuf/field_mask.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
option go_package = "github.com/hashicorp/boundary/internal/gen/controller/api/services;services";
option (custom_options.v1.domain) = "credential";
service CredentialService {
// 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.
rpc GetCredential(GetCredentialRequest) returns (GetCredentialResponse) {
option (google.api.http) = {
get: "/v1/credentials/{id}"
response_body: "item"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Gets a single Credential."
};
}
// 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.
rpc ListCredentials(ListCredentialsRequest) returns (ListCredentialsResponse) {
option (google.api.http) = {
get: "/v1/credentials"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Lists all Credentials."
};
}
// 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.
rpc CreateCredential(CreateCredentialRequest) returns (CreateCredentialResponse) {
option (google.api.http) = {
post: "/v1/credentials"
body: "item"
response_body: "item"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Creates a single Credential."
};
}
// 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.
rpc UpdateCredential(UpdateCredentialRequest) returns (UpdateCredentialResponse) {
option (google.api.http) = {
patch: "/v1/credentials/{id}"
body: "item"
response_body: "item"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Updates a Credential."
};
}
// DeleteCredential removes an Credential from Boundary. If the Credential id
// is malformed or not provided an error is returned.
rpc DeleteCredential(DeleteCredentialRequest) returns (DeleteCredentialResponse) {
option (google.api.http) = {
delete: "/v1/credentials/{id}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Deletes a Credential"
};
}
}
message GetCredentialRequest {
string id = 1; // @gotags: `class:"public"`
}
message GetCredentialResponse {
resources.credentials.v1.Credential item = 1;
}
message ListCredentialsRequest {
string credential_store_id = 1 [json_name = "scope_id"]; // @gotags: `class:"public"`
string filter = 30 [json_name = "filter"]; // @gotags: `class:"public"`
}
message ListCredentialsResponse {
repeated resources.credentials.v1.Credential items = 1;
}
message CreateCredentialRequest {
resources.credentials.v1.Credential item = 1;
}
message CreateCredentialResponse {
string uri = 1; // @gotags: `class:"public"`
resources.credentials.v1.Credential item = 2;
}
message UpdateCredentialRequest {
string id = 1; // @gotags: `class:"public"`
resources.credentials.v1.Credential item = 2;
google.protobuf.FieldMask update_mask = 3 [json_name = "update_mask"];
}
message UpdateCredentialResponse {
resources.credentials.v1.Credential item = 1;
}
message DeleteCredentialRequest {
string id = 1; // @gotags: `class:"public"`
}
message DeleteCredentialResponse {}
@@ -0,0 +1,118 @@
syntax = "proto3";
// Package store provides protobufs for storing types in the static
// credential package.
package controller.storage.credential.static.store.v1;
import "controller/custom_options/v1/options.proto";
import "controller/storage/timestamp/v1/timestamp.proto";
option go_package = "github.com/hashicorp/boundary/internal/credential/static/store;store";
message CredentialStore {
// public_id is a surrogate key suitable for use in a public API.
// @inject_tag: `gorm:"primary_key"`
string public_id = 1;
// The create_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp create_time = 2;
// The update_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp update_time = 3;
// name is optional. If set, it must be unique within scope_id.
// @inject_tag: `gorm:"default:null"`
string name = 4 [(custom_options.v1.mask_mapping) = {
this: "Name"
that: "name"
}];
// description is optional.
// @inject_tag: `gorm:"default:null"`
string description = 5 [(custom_options.v1.mask_mapping) = {
this: "Description"
that: "description"
}];
// The scope_id of the owning scope.
// It must be set.
// @inject_tag: `gorm:"not_null"`
string scope_id = 6;
// version allows optimistic locking of the resource.
// @inject_tag: `gorm:"default:null"`
uint32 version = 7;
}
message UsernamePasswordCredential {
// public_id is a surrogate key suitable for use in a public API.
// @inject_tag: `gorm:"primary_key"`
string public_id = 1;
// create_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp create_time = 2;
// update_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp update_time = 3;
// name is optional. If set, it must be unique within scope_id.
// @inject_tag: `gorm:"default:null"`
string name = 4 [(custom_options.v1.mask_mapping) = {
this: "Name"
that: "name"
}];
// description is optional.
// @inject_tag: `gorm:"default:null"`
string description = 5 [(custom_options.v1.mask_mapping) = {
this: "Description"
that: "description"
}];
// store_id of the owning static credential store.
// It must be set.
// @inject_tag: `gorm:"not_null"`
string store_id = 6;
// version allows optimistic locking of the resource.
// @inject_tag: `gorm:"default:null"`
uint32 version = 7;
// username is the username associated with the credential.
// It must be set.
// @inject_tag: `gorm:"not_null"`
string username = 8 [(custom_options.v1.mask_mapping) = {
this: "Username"
that: "attributes.username"
}];
// 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"`
bytes password = 9 [(custom_options.v1.mask_mapping) = {
this: "Password"
that: "attributes.password"
}];
// 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"`
bytes ct_password = 10;
// password_hmac is a sha256-hmac of the unencrypted password. It is recalculated
// everytime the password is updated.
// @inject_tag: `gorm:"not_null"`
bytes password_hmac = 11 [(custom_options.v1.mask_mapping) = {
this: "PasswordHmac"
that: "attributes.password_hmac"
}];
// The key_id of the kms database key used for encrypting this entry.
// It must be set.
// @inject_tag: `gorm:"not_null"`
string key_id = 12;
}
@@ -0,0 +1,201 @@
package credentials_test
import (
"context"
"encoding/json"
"testing"
"time"
"github.com/hashicorp/boundary/sdk/pbs/controller/api"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
"github.com/hashicorp/boundary/sdk/wrapper"
"github.com/hashicorp/eventlogger"
"github.com/hashicorp/eventlogger/filters/encrypt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
func TestCredentialClassification(t *testing.T) {
ctx := context.Background()
now := time.Now()
pbNow := timestamppb.Now()
wrapper := wrapper.TestWrapper(t)
testEncryptingFilter := api.NewEncryptFilter(t, wrapper)
cases := []struct {
name string
in *eventlogger.Event
want *eventlogger.Event
}{
{
"Usernamepassword",
&eventlogger.Event{
Type: "test",
CreatedAt: now,
Payload: &pb.Credential{
Id: "id",
CredentialStoreId: "cred-store-id",
Scope: &scopes.ScopeInfo{
Id: "scope-id",
Type: "scope-type",
Name: "scope-name",
Description: "scope-descriptione",
ParentScopeId: "scope-parent-scope-id",
},
Name: &wrapperspb.StringValue{
Value: "name",
},
Description: &wrapperspb.StringValue{
Value: "description",
},
CreatedTime: pbNow,
UpdatedTime: pbNow,
Version: 1,
Type: "username_password",
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: &wrapperspb.StringValue{Value: "username"},
Password: &wrapperspb.StringValue{Value: "password"},
PasswordHmac: "password-hmac",
},
},
AuthorizedActions: []string{
"action-1",
"action-2",
},
},
},
&eventlogger.Event{
Type: "test",
CreatedAt: now,
Payload: &pb.Credential{
Id: "id",
CredentialStoreId: "cred-store-id",
Scope: &scopes.ScopeInfo{
Id: "scope-id",
Type: "scope-type",
Name: "scope-name",
Description: "scope-descriptione",
ParentScopeId: "scope-parent-scope-id",
},
Name: &wrapperspb.StringValue{
Value: "name",
},
Description: &wrapperspb.StringValue{
Value: "description",
},
CreatedTime: pbNow,
UpdatedTime: pbNow,
Version: 1,
Type: "username_password",
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: &wrapperspb.StringValue{Value: "username"},
Password: &wrapperspb.StringValue{Value: "[REDACTED]"},
PasswordHmac: "password-hmac",
},
},
AuthorizedActions: []string{
"action-1",
"action-2",
},
},
},
},
{
"Default",
&eventlogger.Event{
Type: "test",
CreatedAt: now,
Payload: &pb.Credential{
Id: "id",
CredentialStoreId: "cred-store-id",
Scope: &scopes.ScopeInfo{
Id: "scope-id",
Type: "scope-type",
Name: "scope-name",
Description: "scope-descriptione",
ParentScopeId: "scope-parent-scope-id",
},
Name: &wrapperspb.StringValue{
Value: "name",
},
Description: &wrapperspb.StringValue{
Value: "description",
},
CreatedTime: pbNow,
UpdatedTime: pbNow,
Version: 1,
Type: "username_password",
Attrs: &pb.Credential_Attributes{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
"field1": structpb.NewStringValue("value-1"),
"field2": structpb.NewStringValue("value-2"),
},
},
},
AuthorizedActions: []string{
"action-1",
"action-2",
},
},
},
&eventlogger.Event{
Type: "test",
CreatedAt: now,
Payload: &pb.Credential{
Id: "id",
CredentialStoreId: "cred-store-id",
Scope: &scopes.ScopeInfo{
Id: "scope-id",
Type: "scope-type",
Name: "scope-name",
Description: "scope-descriptione",
ParentScopeId: "scope-parent-scope-id",
},
Name: &wrapperspb.StringValue{
Value: "name",
},
Description: &wrapperspb.StringValue{
Value: "description",
},
CreatedTime: pbNow,
UpdatedTime: pbNow,
Version: 1,
Type: "username_password",
Attrs: &pb.Credential_Attributes{
Attributes: &structpb.Struct{
Fields: map[string]*structpb.Value{
"field1": structpb.NewStringValue(encrypt.RedactedData),
"field2": structpb.NewStringValue(encrypt.RedactedData),
},
},
},
AuthorizedActions: []string{
"action-1",
"action-2",
},
},
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
wantJSON, err := json.Marshal(tc.want)
require.NoError(t, err)
got, err := testEncryptingFilter.Process(ctx, tc.in)
require.NoError(t, err)
require.NotNil(t, got)
gotJSON, err := json.Marshal(got)
require.NoError(t, err)
assert.JSONEq(t, string(wantJSON), string(gotJSON))
})
}
}
@@ -0,0 +1,208 @@
package credentials_test
import (
"fmt"
"net/http"
"testing"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/boundary/api/credentialstores"
"github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/daemon/controller"
"github.com/hashicorp/boundary/internal/iam"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestList(t *testing.T) {
assert, require := assert.New(t), require.New(t)
tc := controller.NewTestController(t, nil)
defer tc.Shutdown()
client := tc.Client()
token := tc.Token()
client.SetToken(token.Token)
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId())
require.NoError(err)
require.NotNil(cs)
credClient := credentials.NewClient(client)
ul, err := credClient.List(tc.Context(), cs.Item.Id)
require.NoError(err)
assert.Empty(ul.Items)
cred, err := credClient.Create(tc.Context(), static.UsernamePasswordSubtype.String(), cs.Item.Id,
credentials.WithName("0"),
credentials.WithDescription("description"),
credentials.WithUsernamePasswordCredentialUsername("user"),
credentials.WithUsernamePasswordCredentialPassword("pass"))
require.NoError(err)
expected := make([]*credentials.Credential, 10)
expected[0] = cred.Item
ul, err = credClient.List(tc.Context(), cs.Item.Id)
require.NoError(err)
assert.ElementsMatch(comparableCatalogSlice(expected[:1]), comparableCatalogSlice(ul.Items))
for i := 1; i < 10; i++ {
cred, err := credClient.Create(tc.Context(), static.UsernamePasswordSubtype.String(), cs.Item.Id,
credentials.WithName(fmt.Sprintf("%d", i)),
credentials.WithDescription("description"),
credentials.WithUsernamePasswordCredentialUsername("user"),
credentials.WithUsernamePasswordCredentialPassword("pass"))
require.NoError(err)
expected[i] = cred.Item
}
ul, err = credClient.List(tc.Context(), cs.Item.Id)
require.NoError(err)
assert.ElementsMatch(comparableCatalogSlice(expected), comparableCatalogSlice(ul.Items))
filterItem := ul.Items[3]
ul, err = credClient.List(tc.Context(), cs.Item.Id,
credentials.WithFilter(fmt.Sprintf(`"/item/id"==%q`, filterItem.Id)))
require.NoError(err)
assert.Len(ul.Items, 1)
assert.Equal(filterItem.Id, ul.Items[0].Id)
}
func comparableCatalogSlice(in []*credentials.Credential) []credentials.Credential {
var filtered []credentials.Credential
for _, i := range in {
p := credentials.Credential{
Id: i.Id,
Name: i.Name,
Description: i.Description,
CreatedTime: i.CreatedTime,
UpdatedTime: i.UpdatedTime,
Attributes: i.Attributes,
}
filtered = append(filtered, p)
}
return filtered
}
func TestCrud(t *testing.T) {
assert, require := assert.New(t), require.New(t)
tc := controller.NewTestController(t, nil)
defer tc.Shutdown()
client := tc.Client()
token := tc.Token()
client.SetToken(token.Token)
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId())
require.NoError(err)
require.NotNil(cs)
checkResource := func(step string, c *credentials.Credential, wantedName, wantedUser string, wantVersion uint32) {
assert.NotNil(c, "returned no resource", step)
assert.Equal(wantedName, c.Name, step)
gotUser, ok := c.Attributes["username"]
require.True(ok)
assert.Equal(wantedUser, gotUser, step)
assert.Equal(wantVersion, c.Version)
}
credClient := credentials.NewClient(client)
cred, err := credClient.Create(tc.Context(), static.UsernamePasswordSubtype.String(), cs.Item.Id, credentials.WithName("foo"),
credentials.WithUsernamePasswordCredentialUsername("user"), credentials.WithUsernamePasswordCredentialPassword("pass"))
require.NoError(err)
require.NotNil(cs)
checkResource("create", cred.Item, "foo", "user", 1)
cred, err = credClient.Read(tc.Context(), cred.Item.Id)
require.NoError(err)
require.NotNil(cs)
checkResource("read", cred.Item, "foo", "user", 1)
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithName("bar"))
require.NoError(err)
require.NotNil(cs)
checkResource("update", cred.Item, "bar", "user", 2)
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithUsernamePasswordCredentialUsername("newuser"))
require.NoError(err)
require.NotNil(cs)
checkResource("update", cred.Item, "bar", "newuser", 3)
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.DefaultName())
require.NoError(err)
require.NotNil(cs)
checkResource("update", cred.Item, "", "newuser", 4)
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version,
credentials.WithName("newuser"), credentials.WithUsernamePasswordCredentialUsername("neweruser"))
require.NoError(err)
require.NotNil(cs)
checkResource("update", cred.Item, "newuser", "neweruser", 5)
_, err = credClient.Delete(tc.Context(), cred.Item.Id)
assert.NoError(err)
_, err = credClient.Delete(tc.Context(), cred.Item.Id)
require.Error(err)
apiErr := api.AsServerError(err)
assert.NotNil(apiErr)
assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode())
}
func TestErrors(t *testing.T) {
assert, require := assert.New(t), require.New(t)
tc := controller.NewTestController(t, nil)
defer tc.Shutdown()
client := tc.Client()
token := tc.Token()
client.SetToken(token.Token)
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId())
require.NoError(err)
require.NotNil(cs)
c := credentials.NewClient(client)
cred, err := c.Create(tc.Context(), static.UsernamePasswordSubtype.String(), cs.Item.Id, credentials.WithName("foo"),
credentials.WithUsernamePasswordCredentialUsername("user"), credentials.WithUsernamePasswordCredentialPassword("pass"))
require.NoError(err)
require.NotNil(cred)
// A malformed id is processed as the id and not a different path to the api.
_, err = c.Read(tc.Context(), fmt.Sprintf("%s/../", cred.Item.Id))
require.Error(err)
apiErr := api.AsServerError(err)
require.NotNil(apiErr)
assert.EqualValues(http.StatusBadRequest, apiErr.Response().StatusCode())
require.Len(apiErr.Details.RequestFields, 1)
assert.Equal(apiErr.Details.RequestFields[0].Name, "id")
// Updating the wrong version should fail.
_, err = c.Update(tc.Context(), cred.Item.Id, 73, credentials.WithName("anything"))
require.Error(err)
apiErr = api.AsServerError(err)
assert.NotNil(apiErr)
assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode())
// Same name
_, err = c.Create(tc.Context(), static.UsernamePasswordSubtype.String(), proj.GetPublicId(), credentials.WithName("foo"))
require.Error(err)
apiErr = api.AsServerError(err)
assert.NotNil(apiErr)
_, err = c.Read(tc.Context(), static.CredentialPrefix+"_doesntexis")
require.Error(err)
apiErr = api.AsServerError(err)
assert.NotNil(apiErr)
assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode())
_, err = c.Read(tc.Context(), "invalid id")
require.Error(err)
apiErr = api.AsServerError(err)
assert.NotNil(apiErr)
assert.EqualValues(http.StatusBadRequest, apiErr.Response().StatusCode())
}
@@ -49,12 +49,17 @@ func TestList(t *testing.T) {
require.NoError(err)
assert.ElementsMatch(comparableCatalogSlice(expected[:1]), comparableCatalogSlice(ul.Items))
for i := 1; i < 10; i++ {
for i := 1; i < 5; i++ {
cr, err = storeClient.Create(tc.Context(), "vault", proj.GetPublicId(), credentialstores.WithName(expected[i].Name),
credentialstores.WithVaultCredentialStoreAddress(vaultServ.Addr), credentialstores.WithVaultCredentialStoreToken(expected[i].Attributes["token"].(string)))
require.NoError(err)
expected[i] = cr.Item
}
for i := 5; i < 10; i++ {
cr, err = storeClient.Create(tc.Context(), "static", proj.GetPublicId(), credentialstores.WithName(expected[i].Name))
require.NoError(err)
expected[i] = cr.Item
}
ul, err = storeClient.List(tc.Context(), proj.GetPublicId())
require.NoError(err)
assert.ElementsMatch(comparableCatalogSlice(expected), comparableCatalogSlice(ul.Items))
@@ -0,0 +1,30 @@
load _authorized_actions
function create_static_credential_store() {
local name=$1
local sid=$2
boundary credential-stores create static \
-name $name \
-description 'test static credential store' \
-scope-id $sid
}
function read_credential_store() {
boundary credential-stores read -id $1 -format json
}
function delete_credential_store() {
boundary credential-stores delete -id $1
}
function list_credential_stores() {
boundary credential-stores list -scope-id $1 -format json
}
function credential_store_id() {
local name=$1
local sid=$2
strip $(list_credential_stores $sid | jq -c ".items[] | select(.name | contains(\"$name\")) | .[\"id\"]")
}
@@ -0,0 +1,34 @@
load _authorized_actions
function create_username_password_credential() {
local name=$1
local sid=$2
local user=$3
local pass=$4
boundary credentials create username-password \
-name $name \
-description 'test username password credential' \
-credential-store-id $sid \
-username $user \
-password $pass
}
function read_credential() {
boundary credentials read -id $1 -format json
}
function delete_credential() {
boundary credentials delete -id $1
}
function list_credentials() {
boundary credentials list -credential-store-id $1 -format json
}
function credential_id() {
local name=$1
local sid=$2
strip $(list_credentials $sid | jq -c ".items[] | select(.name | contains(\"$name\")) | .[\"id\"]")
}
@@ -0,0 +1,47 @@
#!/usr/bin/env bats
load _auth
load _helpers
load _credential_stores
export NEW_STORE='test'
@test "boundary/login: can login as default user" {
run login $DEFAULT_LOGIN
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/credential-stores: can create $NEW_STORE static store in default project" {
run create_static_credential_store $NEW_STORE $DEFAULT_P_ID
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/credential-stores: can not create already created $NEW_STORE static store" {
run create_static_credential_store $NEW_STORE $DEFAULT_P_ID
echo "$output"
[ "$status" -eq 1 ]
}
@test "boundary/credential-stores: can read $NEW_STORE static store" {
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
run read_credential_store $csid
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/credential-stores: can delete $NEW_STORE static store" {
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
run delete_credential_store $csid
echo "$output"
run has_status_code "$output" "204"
[ "$status" -eq 0 ]
}
@test "boundary/credential-stores: can not read deleted $NEW_STORE static store" {
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
run read_credential_store $csid
echo "$output"
[ "$status" -eq 1 ]
}
@@ -0,0 +1,60 @@
#!/usr/bin/env bats
load _auth
load _helpers
load _credential_stores
load _credentials
export NEW_STORE='credentials-test-store'
export NEW_CREDENTIAL='test'
@test "boundary/login: can login as default user" {
run login $DEFAULT_LOGIN
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/credential-stores: can create $NEW_STORE static store in default project" {
run create_static_credential_store $NEW_STORE $DEFAULT_P_ID
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/credentials: can create $NEW_CREDENTIAL credential in $NEW_STORE store" {
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
run create_username_password_credential $NEW_CREDENTIAL $csid 'username' 'password'
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/credentials: can not create already created $NEW_CREDENTIAL credential" {
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
run create_username_password_credential $NEW_CREDENTIAL $csid 'username' 'password'
echo "$output"
[ "$status" -eq 1 ]
}
@test "boundary/credentials: can read $NEW_CREDENTIAL credential" {
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
local cid=$(credential_id $NEW_CREDENTIAL $csid)
run read_credential $cid
echo "$output"
[ "$status" -eq 0 ]
}
@test "boundary/credentials: can delete $NEW_CREDENTIAL credential" {
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
local cid=$(credential_id $NEW_CREDENTIAL $csid)
run delete_credential $cid
echo "$output"
run has_status_code "$output" "204"
[ "$status" -eq 0 ]
}
@test "boundary/credential-stores: can not read deleted $NEW_CREDENTIAL credential" {
local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID)
local cid=$(credential_id $NEW_CREDENTIAL $csid)
run read_credential $cid
echo "$output"
[ "$status" -eq 1 ]
}
+3
View File
@@ -25,6 +25,7 @@ const (
ManagedGroup
CredentialStore
CredentialLibrary
Credential
// NOTE: When adding a new type, be sure to update:
//
// * The Grant.validateType function and test
@@ -58,6 +59,7 @@ func (r Type) String() string {
"managed-group",
"credential-store",
"credential-library",
"credential",
}[r]
}
@@ -90,4 +92,5 @@ var Map = map[string]Type{
ManagedGroup.String(): ManagedGroup,
CredentialStore.String(): CredentialStore,
CredentialLibrary.String(): CredentialLibrary,
Credential.String(): Credential,
}
@@ -0,0 +1,462 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc (unknown)
// source: controller/api/resources/credentials/v1/credential.proto
package credentials
import (
scopes "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
_ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions"
_ "google.golang.org/genproto/googleapis/api/visibility"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
structpb "google.golang.org/protobuf/types/known/structpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
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)
)
// Credential contains all fields related to an Credential resource
type Credential struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Output only. The ID of the Credential.
Id string `protobuf:"bytes,10,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"`
// The ID of the Credential Store of which this Credential is a part.
CredentialStoreId string `protobuf:"bytes,20,opt,name=credential_store_id,proto3" json:"credential_store_id,omitempty" class:"public"` // @gotags: `class:"public"`
// Output only. Scope information for this Credential.
Scope *scopes.ScopeInfo `protobuf:"bytes,30,opt,name=scope,proto3" json:"scope,omitempty"`
// Optional name for identification purposes.
Name *wrapperspb.StringValue `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty" class:"public"` // @gotags: `class:"public"`
// Optional user-set description for identification purposes.
Description *wrapperspb.StringValue `protobuf:"bytes,50,opt,name=description,proto3" json:"description,omitempty" class:"public"` // @gotags: `class:"public"`
// Output only. The time this resource was created.
CreatedTime *timestamppb.Timestamp `protobuf:"bytes,60,opt,name=created_time,proto3" json:"created_time,omitempty" class:"public"` // @gotags: `class:"public"`
// Output only. The time this resource was last updated.
UpdatedTime *timestamppb.Timestamp `protobuf:"bytes,70,opt,name=updated_time,proto3" json:"updated_time,omitempty" class:"public"` // @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.
Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"`
// The Credential type.
Type string `protobuf:"bytes,90,opt,name=type,proto3" json:"type,omitempty" class:"public"` // @gotags: `class:"public"`
// Types that are assignable to Attrs:
// *Credential_Attributes
// *Credential_UsernamePasswordAttributes
Attrs isCredential_Attrs `protobuf_oneof:"attrs"`
// Output only. The available actions on this resource for this user.
AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *Credential) Reset() {
*x = Credential{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Credential) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Credential) ProtoMessage() {}
func (x *Credential) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_resources_credentials_v1_credential_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 Credential.ProtoReflect.Descriptor instead.
func (*Credential) Descriptor() ([]byte, []int) {
return file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP(), []int{0}
}
func (x *Credential) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *Credential) GetCredentialStoreId() string {
if x != nil {
return x.CredentialStoreId
}
return ""
}
func (x *Credential) GetScope() *scopes.ScopeInfo {
if x != nil {
return x.Scope
}
return nil
}
func (x *Credential) GetName() *wrapperspb.StringValue {
if x != nil {
return x.Name
}
return nil
}
func (x *Credential) GetDescription() *wrapperspb.StringValue {
if x != nil {
return x.Description
}
return nil
}
func (x *Credential) GetCreatedTime() *timestamppb.Timestamp {
if x != nil {
return x.CreatedTime
}
return nil
}
func (x *Credential) GetUpdatedTime() *timestamppb.Timestamp {
if x != nil {
return x.UpdatedTime
}
return nil
}
func (x *Credential) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
func (x *Credential) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (m *Credential) GetAttrs() isCredential_Attrs {
if m != nil {
return m.Attrs
}
return nil
}
func (x *Credential) GetAttributes() *structpb.Struct {
if x, ok := x.GetAttrs().(*Credential_Attributes); ok {
return x.Attributes
}
return nil
}
func (x *Credential) GetUsernamePasswordAttributes() *UsernamePasswordAttributes {
if x, ok := x.GetAttrs().(*Credential_UsernamePasswordAttributes); ok {
return x.UsernamePasswordAttributes
}
return nil
}
func (x *Credential) GetAuthorizedActions() []string {
if x != nil {
return x.AuthorizedActions
}
return nil
}
type isCredential_Attrs interface {
isCredential_Attrs()
}
type Credential_Attributes struct {
// The attributes that are applicable for the specific Credential type.
Attributes *structpb.Struct `protobuf:"bytes,100,opt,name=attributes,proto3,oneof"`
}
type Credential_UsernamePasswordAttributes struct {
UsernamePasswordAttributes *UsernamePasswordAttributes `protobuf:"bytes,101,opt,name=username_password_attributes,json=usernamePasswordAttributes,proto3,oneof"`
}
func (*Credential_Attributes) isCredential_Attrs() {}
func (*Credential_UsernamePasswordAttributes) isCredential_Attrs() {}
// The attributes of a UsernamePassword Credential.
type UsernamePasswordAttributes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The username associated with the credential.
Username *wrapperspb.StringValue `protobuf:"bytes,10,opt,name=username,proto3" json:"username,omitempty" class:"public"` // @gotags: `class:"public"`
// Input only. The password associated with the credential.
Password *wrapperspb.StringValue `protobuf:"bytes,20,opt,name=password,proto3" json:"password,omitempty" class:"secret"` // @gotags: `class:"secret"`
// Output only. The hmac value of the password.
PasswordHmac string `protobuf:"bytes,30,opt,name=password_hmac,proto3" json:"password_hmac,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *UsernamePasswordAttributes) Reset() {
*x = UsernamePasswordAttributes{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *UsernamePasswordAttributes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UsernamePasswordAttributes) ProtoMessage() {}
func (x *UsernamePasswordAttributes) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_resources_credentials_v1_credential_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 UsernamePasswordAttributes.ProtoReflect.Descriptor instead.
func (*UsernamePasswordAttributes) Descriptor() ([]byte, []int) {
return file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP(), []int{1}
}
func (x *UsernamePasswordAttributes) GetUsername() *wrapperspb.StringValue {
if x != nil {
return x.Username
}
return nil
}
func (x *UsernamePasswordAttributes) GetPassword() *wrapperspb.StringValue {
if x != nil {
return x.Password
}
return nil
}
func (x *UsernamePasswordAttributes) GetPasswordHmac() string {
if x != nil {
return x.PasswordHmac
}
return ""
}
var File_controller_api_resources_credentials_v1_credential_proto protoreflect.FileDescriptor
var file_controller_api_resources_credentials_v1_credential_proto_rawDesc = []byte{
0x0a, 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, 0x12, 0x27, 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, 0x1a, 0x2e, 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, 0x73, 0x63,
0x6f, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 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,
0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x69, 0x73, 0x69,
0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74,
0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61,
0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa8, 0x06, 0x0a, 0x0a,
0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x63, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69,
0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05,
0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 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, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31,
0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70,
0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0,
0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x4e,
0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73,
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda,
0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a,
0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a,
0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x4a, 0x0a, 0x0a, 0x61,
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x0f, 0xa0, 0xda, 0x29, 0x01, 0x9a, 0xe3,
0x29, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x74, 0x74,
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0xb2, 0x01, 0x0a, 0x1c, 0x75, 0x73, 0x65, 0x72,
0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x61, 0x74,
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x43,
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, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d,
0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
0x74, 0x65, 0x73, 0x42, 0x29, 0xa0, 0xda, 0x29, 0x01, 0x9a, 0xe3, 0x29, 0x11, 0x75, 0x73, 0x65,
0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0xfa, 0xd2,
0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x48, 0x00,
0x52, 0x1a, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x72, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12,
0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f,
0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x07, 0x0a,
0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x22, 0xb6, 0x02, 0x0a, 0x1a, 0x55, 0x73, 0x65, 0x72, 0x6e,
0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69,
0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x61, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x27, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1f, 0x0a,
0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x08,
0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x61, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73,
0x77, 0x6f, 0x72, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x27, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd,
0x29, 0x1f, 0x0a, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x08, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x52, 0x0a, 0x0d, 0x70,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x1e, 0x20, 0x01,
0x28, 0x09, 0x42, 0x2c, 0xc2, 0xdd, 0x29, 0x28, 0x0a, 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, 0x12, 0x0c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x6d, 0x61, 0x63,
0x52, 0x0d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x42,
0x58, 0x5a, 0x56, 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, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 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, 0x3b, 0x63, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_controller_api_resources_credentials_v1_credential_proto_rawDescOnce sync.Once
file_controller_api_resources_credentials_v1_credential_proto_rawDescData = file_controller_api_resources_credentials_v1_credential_proto_rawDesc
)
func file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP() []byte {
file_controller_api_resources_credentials_v1_credential_proto_rawDescOnce.Do(func() {
file_controller_api_resources_credentials_v1_credential_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_resources_credentials_v1_credential_proto_rawDescData)
})
return file_controller_api_resources_credentials_v1_credential_proto_rawDescData
}
var file_controller_api_resources_credentials_v1_credential_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_controller_api_resources_credentials_v1_credential_proto_goTypes = []interface{}{
(*Credential)(nil), // 0: controller.api.resources.credentials.v1.Credential
(*UsernamePasswordAttributes)(nil), // 1: controller.api.resources.credentials.v1.UsernamePasswordAttributes
(*scopes.ScopeInfo)(nil), // 2: controller.api.resources.scopes.v1.ScopeInfo
(*wrapperspb.StringValue)(nil), // 3: google.protobuf.StringValue
(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
(*structpb.Struct)(nil), // 5: google.protobuf.Struct
}
var file_controller_api_resources_credentials_v1_credential_proto_depIdxs = []int32{
2, // 0: controller.api.resources.credentials.v1.Credential.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
3, // 1: controller.api.resources.credentials.v1.Credential.name:type_name -> google.protobuf.StringValue
3, // 2: controller.api.resources.credentials.v1.Credential.description:type_name -> google.protobuf.StringValue
4, // 3: controller.api.resources.credentials.v1.Credential.created_time:type_name -> google.protobuf.Timestamp
4, // 4: controller.api.resources.credentials.v1.Credential.updated_time:type_name -> google.protobuf.Timestamp
5, // 5: controller.api.resources.credentials.v1.Credential.attributes:type_name -> google.protobuf.Struct
1, // 6: controller.api.resources.credentials.v1.Credential.username_password_attributes:type_name -> controller.api.resources.credentials.v1.UsernamePasswordAttributes
3, // 7: controller.api.resources.credentials.v1.UsernamePasswordAttributes.username:type_name -> google.protobuf.StringValue
3, // 8: controller.api.resources.credentials.v1.UsernamePasswordAttributes.password:type_name -> google.protobuf.StringValue
9, // [9:9] is the sub-list for method output_type
9, // [9:9] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
}
func init() { file_controller_api_resources_credentials_v1_credential_proto_init() }
func file_controller_api_resources_credentials_v1_credential_proto_init() {
if File_controller_api_resources_credentials_v1_credential_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_controller_api_resources_credentials_v1_credential_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Credential); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_resources_credentials_v1_credential_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UsernamePasswordAttributes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_controller_api_resources_credentials_v1_credential_proto_msgTypes[0].OneofWrappers = []interface{}{
(*Credential_Attributes)(nil),
(*Credential_UsernamePasswordAttributes)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_api_resources_credentials_v1_credential_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_controller_api_resources_credentials_v1_credential_proto_goTypes,
DependencyIndexes: file_controller_api_resources_credentials_v1_credential_proto_depIdxs,
MessageInfos: file_controller_api_resources_credentials_v1_credential_proto_msgTypes,
}.Build()
File_controller_api_resources_credentials_v1_credential_proto = out.File
file_controller_api_resources_credentials_v1_credential_proto_rawDesc = nil
file_controller_api_resources_credentials_v1_credential_proto_goTypes = nil
file_controller_api_resources_credentials_v1_credential_proto_depIdxs = nil
}