Merge remote-tracking branch 'origin/main' into llb-byow
This commit is contained in:
commit
704d68848c
160 files changed
+15933
-4684
No files matched your search
@@ -3,6 +3,7 @@ package common
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
credstatic "github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
pluginhost "github.com/hashicorp/boundary/internal/host/plugin"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
type (
|
||||
AuthTokenRepoFactory = oidc.AuthTokenRepoFactory
|
||||
VaultCredentialRepoFactory = func() (*vault.Repository, error)
|
||||
StaticCredentialRepoFactory = func() (*credstatic.Repository, error)
|
||||
IamRepoFactory func() (*iam.Repository, error)
|
||||
OidcAuthRepoFactory = oidc.OidcRepoFactory
|
||||
PasswordAuthRepoFactory func() (*password.Repository, error)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/cmd/config"
|
||||
credstatic "github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/health"
|
||||
@@ -67,6 +68,7 @@ type Controller struct {
|
||||
// Repo factory methods
|
||||
AuthTokenRepoFn common.AuthTokenRepoFactory
|
||||
VaultCredentialRepoFn common.VaultCredentialRepoFactory
|
||||
StaticCredentialRepoFn common.StaticCredentialRepoFactory
|
||||
IamRepoFn common.IamRepoFactory
|
||||
OidcRepoFn common.OidcAuthRepoFactory
|
||||
PasswordAuthRepoFn common.PasswordAuthRepoFactory
|
||||
@@ -249,9 +251,8 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
|
||||
jobRepoFn := func() (*job.Repository, error) {
|
||||
return job.NewRepository(dbase, dbase, c.kms)
|
||||
}
|
||||
// TODO: the RunJobsLimit is temporary until a better fix gets in. This
|
||||
// currently caps the scheduler at running 10 jobs per interval.
|
||||
schedulerOpts := []scheduler.Option{scheduler.WithRunJobsLimit(20)}
|
||||
// TODO: Allow setting run jobs limit from config
|
||||
schedulerOpts := []scheduler.Option{scheduler.WithRunJobsLimit(-1)}
|
||||
if c.conf.RawConfig.Controller.SchedulerRunJobInterval > 0 {
|
||||
schedulerOpts = append(schedulerOpts, scheduler.WithRunJobsInterval(c.conf.RawConfig.Controller.SchedulerRunJobInterval))
|
||||
}
|
||||
@@ -279,6 +280,9 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
|
||||
c.VaultCredentialRepoFn = func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(dbase, dbase, c.kms, c.scheduler)
|
||||
}
|
||||
c.StaticCredentialRepoFn = func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(ctx, dbase, dbase, c.kms)
|
||||
}
|
||||
c.ServersRepoFn = func() (*servers.Repository, error) {
|
||||
return servers.NewRepository(dbase, dbase, c.kms)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,17 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/cmd/config"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
host_plugin_assets "github.com/hashicorp/boundary/plugins/host"
|
||||
"github.com/hashicorp/go-secure-stdlib/listenerutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -223,3 +228,39 @@ func TestControllerNewListenerConfig(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestController_NewPluginsConfig(t *testing.T) {
|
||||
require := require.New(t)
|
||||
testCtx := context.Background()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
tc := &TestController{
|
||||
t: t,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
opts: nil,
|
||||
}
|
||||
|
||||
initialConfig, err := config.DevController()
|
||||
require.NoError(err)
|
||||
tmpDir := t.TempDir()
|
||||
initialConfig.Plugins.ExecutionDir = tmpDir
|
||||
conf := TestControllerConfig(t, ctx, tc, &TestControllerOpts{Config: initialConfig})
|
||||
conf.EnabledPlugins = []base.EnabledPlugin{
|
||||
base.EnabledPluginHostAws,
|
||||
base.EnabledPluginHostAzure,
|
||||
}
|
||||
|
||||
_, err = New(testCtx, conf)
|
||||
require.NoError(err)
|
||||
|
||||
// Check that both plugins were written to the temp dir
|
||||
files, err := os.ReadDir(tmpDir)
|
||||
require.NoError(err)
|
||||
require.Len(files, 2)
|
||||
var pluginNames []string
|
||||
for _, file := range files {
|
||||
pluginNames = append(pluginNames, filepath.Base(file.Name()))
|
||||
}
|
||||
expectedPluginNames := []string{host_plugin_assets.HostPluginPrefix + "aws.gz", host_plugin_assets.HostPluginPrefix + "azure.gz"}
|
||||
require.Empty(cmp.Diff(expectedPluginNames, pluginNames))
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/authmethods"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/authtokens"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentials"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentialstores"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/groups"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/health"
|
||||
@@ -175,7 +176,8 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
|
||||
c.SessionRepoFn,
|
||||
c.PluginHostRepoFn,
|
||||
c.StaticHostRepoFn,
|
||||
c.VaultCredentialRepoFn)
|
||||
c.VaultCredentialRepoFn,
|
||||
c.StaticCredentialRepoFn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create target handler service: %w", err)
|
||||
}
|
||||
@@ -210,7 +212,7 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
|
||||
services.RegisterManagedGroupServiceServer(s, mgs)
|
||||
}
|
||||
if _, ok := currentServices[services.CredentialStoreService_ServiceDesc.ServiceName]; !ok {
|
||||
cs, err := credentialstores.NewService(c.VaultCredentialRepoFn, c.IamRepoFn)
|
||||
cs, err := credentialstores.NewService(c.VaultCredentialRepoFn, c.StaticCredentialRepoFn, c.IamRepoFn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create credential store handler service: %w", err)
|
||||
}
|
||||
@@ -230,6 +232,13 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
|
||||
}
|
||||
services.RegisterWorkerServiceServer(s, ws)
|
||||
}
|
||||
if _, ok := currentServices[services.CredentialService_ServiceDesc.ServiceName]; !ok {
|
||||
c, err := credentials.NewService(c.StaticCredentialRepoFn, c.IamRepoFn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create credential handler service: %w", err)
|
||||
}
|
||||
services.RegisterCredentialServiceServer(s, c)
|
||||
}
|
||||
if _, ok := s.GetServiceInfo()[opsservices.HealthService_ServiceDesc.ServiceName]; !ok {
|
||||
hs := health.NewService()
|
||||
opsservices.RegisterHealthServiceServer(s, hs)
|
||||
@@ -290,6 +299,9 @@ func registerGrpcGatewayEndpoints(ctx context.Context, gwMux *runtime.ServeMux,
|
||||
if err := services.RegisterWorkerServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil {
|
||||
return fmt.Errorf("failed to register worker service handler: %w", err)
|
||||
}
|
||||
if err := services.RegisterCredentialServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil {
|
||||
return fmt.Errorf("failed to register credential service handler: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -163,6 +163,12 @@ func TestHandleImplementedPaths(t *testing.T) {
|
||||
"v1/targets/someid:add-host-sets",
|
||||
"v1/targets/someid:set-host-sets",
|
||||
"v1/targets/someid:remove-host-sets",
|
||||
"v1/targets/someid:add-host-sources",
|
||||
"v1/targets/someid:set-host-sources",
|
||||
"v1/targets/someid:remove-host-sources",
|
||||
"v1/targets/someid:add-credential-sources",
|
||||
"v1/targets/someid:set-credential-sources",
|
||||
"v1/targets/someid:remove-credential-sources",
|
||||
"v1/users/someid:add-accounts",
|
||||
"v1/users/someid:set-accounts",
|
||||
"v1/users/someid:remove-accounts",
|
||||
|
||||
@@ -1265,7 +1265,9 @@ func transformAuthenticateRequestAttributes(msg proto.Message) error {
|
||||
return fmt.Errorf("%s: unknown command %q", op, authRequest.GetCommand())
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s: unknown auth method subtype in ID %q", op, authRequest.GetAuthMethodId())
|
||||
return &subtypes.UnknownSubtypeIDError{
|
||||
ID: authRequest.GetAuthMethodId(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+3
-3
@@ -579,7 +579,7 @@ func toStorageVaultLibrary(storeId string, in *pb.CredentialLibrary) (out *vault
|
||||
|
||||
credentialType := credential.Type(in.GetCredentialType())
|
||||
switch credentialType {
|
||||
case credential.UserPasswordType:
|
||||
case credential.UsernamePasswordType:
|
||||
opts = append(opts, vault.WithCredentialType(credentialType))
|
||||
overrides := in.CredentialMappingOverrides.AsMap()
|
||||
var mapOpts []vault.Option
|
||||
@@ -694,7 +694,7 @@ func validateMapping(badFields map[string]string, credentialType credential.Type
|
||||
badFields[globals.CredentialMappingOverridesField] = fmt.Sprintf("This field can only be set if %q is set", globals.CredentialTypeField)
|
||||
}
|
||||
return
|
||||
case credential.UserPasswordType:
|
||||
case credential.UsernamePasswordType:
|
||||
validFields[usernameAttribute] = true
|
||||
validFields[passwordAttribute] = true
|
||||
default:
|
||||
@@ -734,7 +734,7 @@ func getMappingUpdates(credentialType credential.Type, current vault.MappingOver
|
||||
}
|
||||
|
||||
switch credentialType {
|
||||
case credential.UserPasswordType:
|
||||
case credential.UsernamePasswordType:
|
||||
var currentUser, currentPass interface{}
|
||||
if overrides, ok := current.(*vault.UserPasswordOverride); ok {
|
||||
currentUser = overrides.UsernameAttribute
|
||||
|
||||
+22
-22
@@ -318,7 +318,7 @@ func TestCreate(t *testing.T) {
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Invalid user_password mapping",
|
||||
name: "Invalid username_password mapping",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
@@ -326,7 +326,7 @@ func TestCreate(t *testing.T) {
|
||||
Path: wrapperspb.String("something"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "user-test",
|
||||
@@ -410,7 +410,7 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid vault CredentialLibrary user_password type",
|
||||
name: "Create a valid vault CredentialLibrary username_password type",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
@@ -418,7 +418,7 @@ func TestCreate(t *testing.T) {
|
||||
Path: wrapperspb.String("something"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
}},
|
||||
idPrefix: vault.CredentialLibraryPrefix + "_",
|
||||
res: &pbs.CreateCredentialLibraryResponse{
|
||||
@@ -437,13 +437,13 @@ func TestCreate(t *testing.T) {
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid vault CredentialLibrary user_password type with username mapping",
|
||||
name: "Create a valid vault CredentialLibrary username_password type with username mapping",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
@@ -459,7 +459,7 @@ func TestCreate(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
}},
|
||||
idPrefix: vault.CredentialLibraryPrefix + "_",
|
||||
res: &pbs.CreateCredentialLibraryResponse{
|
||||
@@ -478,7 +478,7 @@ func TestCreate(t *testing.T) {
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "user-test",
|
||||
@@ -492,7 +492,7 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid vault CredentialLibrary user_password type with username/password mapping",
|
||||
name: "Create a valid vault CredentialLibrary username_password type with username/password mapping",
|
||||
req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
@@ -509,7 +509,7 @@ func TestCreate(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
}},
|
||||
idPrefix: vault.CredentialLibraryPrefix + "_",
|
||||
res: &pbs.CreateCredentialLibraryResponse{
|
||||
@@ -528,7 +528,7 @@ func TestCreate(t *testing.T) {
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
},
|
||||
CredentialType: string(credential.UserPasswordType),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "user-test",
|
||||
@@ -607,7 +607,7 @@ func TestGet(t *testing.T) {
|
||||
repo, err := repoFn()
|
||||
require.NoError(t, err)
|
||||
lib, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path",
|
||||
vault.WithCredentialType("user_password"),
|
||||
vault.WithCredentialType("username_password"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewUserPasswordOverride(
|
||||
vault.WithOverrideUsernameAttribute("user"),
|
||||
@@ -664,7 +664,7 @@ func TestGet(t *testing.T) {
|
||||
HttpMethod: wrapperspb.String(userPassLib.GetHttpMethod()),
|
||||
},
|
||||
},
|
||||
CredentialType: "user_password",
|
||||
CredentialType: "username_password",
|
||||
CredentialMappingOverrides: func() *structpb.Struct {
|
||||
v := map[string]interface{}{
|
||||
usernameAttribute: "user",
|
||||
@@ -903,7 +903,7 @@ func TestUpdate(t *testing.T) {
|
||||
{
|
||||
name: "user-password-attributes-change-username-attribute",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("user_password"),
|
||||
vault.WithCredentialType("username_password"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewUserPasswordOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
@@ -932,7 +932,7 @@ func TestUpdate(t *testing.T) {
|
||||
{
|
||||
name: "user-password-attributes-change-password-attribute",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("user_password"),
|
||||
vault.WithCredentialType("username_password"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewUserPasswordOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
@@ -961,7 +961,7 @@ func TestUpdate(t *testing.T) {
|
||||
{
|
||||
name: "user-password-attributes-change-username-and-password-attributes",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("user_password"),
|
||||
vault.WithCredentialType("username_password"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewUserPasswordOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
@@ -992,7 +992,7 @@ func TestUpdate(t *testing.T) {
|
||||
{
|
||||
name: "no-mapping-override-change-username-and-password-attributes",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("user_password"),
|
||||
vault.WithCredentialType("username_password"),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(passwordAttrField, usernameAttrField),
|
||||
@@ -1023,7 +1023,7 @@ func TestUpdate(t *testing.T) {
|
||||
{
|
||||
name: "user-password-attributes-delete-mapping-override",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("user_password"),
|
||||
vault.WithCredentialType("username_password"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewUserPasswordOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
@@ -1045,7 +1045,7 @@ func TestUpdate(t *testing.T) {
|
||||
{
|
||||
name: "no-mapping-override-delete-mapping-override",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("user_password"),
|
||||
vault.WithCredentialType("username_password"),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(credentialMappingPathField),
|
||||
@@ -1062,7 +1062,7 @@ func TestUpdate(t *testing.T) {
|
||||
{
|
||||
name: "user-password-attributes-delete-mapping-override-field-specific",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("user_password"),
|
||||
vault.WithCredentialType("username_password"),
|
||||
vault.WithMappingOverride(
|
||||
vault.NewUserPasswordOverride(
|
||||
vault.WithOverrideUsernameAttribute("orig-user"),
|
||||
@@ -1092,7 +1092,7 @@ func TestUpdate(t *testing.T) {
|
||||
{
|
||||
name: "no-mapping-override-delete-mapping-override-field-specific",
|
||||
opts: []vault.Option{
|
||||
vault.WithCredentialType("user_password"),
|
||||
vault.WithCredentialType("username_password"),
|
||||
},
|
||||
req: &pbs.UpdateCredentialLibraryRequest{
|
||||
UpdateMask: fieldmask(passwordAttrField, usernameAttrField),
|
||||
@@ -1185,7 +1185,7 @@ func TestUpdate(t *testing.T) {
|
||||
{
|
||||
name: "read only credential type",
|
||||
path: "credential_type",
|
||||
item: &pb.CredentialLibrary{CredentialType: string(credential.UserPasswordType)},
|
||||
item: &pb.CredentialLibrary{CredentialType: string(credential.UsernamePasswordType)},
|
||||
},
|
||||
}
|
||||
for _, tc := range errCases {
|
||||
|
||||
@@ -0,0 +1,592 @@
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/credential/static/store"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
const (
|
||||
usernameField = "attributes.username"
|
||||
passwordField = "attributes.password"
|
||||
domain = "credential"
|
||||
)
|
||||
|
||||
var (
|
||||
maskManager handlers.MaskManager
|
||||
|
||||
// IdActions contains the set of actions that can be performed on
|
||||
// individual resources
|
||||
IdActions = action.ActionSet{
|
||||
action.NoOp,
|
||||
action.Read,
|
||||
action.Update,
|
||||
action.Delete,
|
||||
}
|
||||
|
||||
// CollectionActions contains the set of actions that can be performed on
|
||||
// this collection
|
||||
CollectionActions = action.ActionSet{
|
||||
action.Create,
|
||||
action.List,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
if maskManager, err = handlers.NewMaskManager(handlers.MaskDestination{&store.UsernamePasswordCredential{}},
|
||||
handlers.MaskSource{&pb.Credential{}, &pb.UsernamePasswordAttributes{}}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Service handles request as described by the pbs.CredentialServiceServer interface.
|
||||
type Service struct {
|
||||
pbs.UnimplementedCredentialServiceServer
|
||||
|
||||
iamRepoFn common.IamRepoFactory
|
||||
repoFn common.StaticCredentialRepoFactory
|
||||
}
|
||||
|
||||
// NewService returns a credential service which handles credential related requests to boundary.
|
||||
func NewService(repo common.StaticCredentialRepoFactory, iamRepo common.IamRepoFactory) (Service, error) {
|
||||
const op = "credentials.NewService"
|
||||
if iamRepo == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing iam repository")
|
||||
}
|
||||
if repo == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static credential repository")
|
||||
}
|
||||
return Service{iamRepoFn: iamRepo, repoFn: repo}, nil
|
||||
}
|
||||
|
||||
var _ pbs.CredentialServiceServer = Service{}
|
||||
|
||||
// ListCredentials implements the interface pbs.CredentialServiceServer
|
||||
func (s Service) ListCredentials(ctx context.Context, req *pbs.ListCredentialsRequest) (*pbs.ListCredentialsResponse, error) {
|
||||
if err := validateListRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetCredentialStoreId(), action.List)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
|
||||
creds, err := s.listFromRepo(ctx, req.GetCredentialStoreId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(creds) == 0 {
|
||||
return &pbs.ListCredentialsResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.Credential, 0, len(creds))
|
||||
res := perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.Credential,
|
||||
Pin: req.GetCredentialStoreId(),
|
||||
}
|
||||
for _, item := range creds {
|
||||
res.Id = item.GetPublicId()
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
|
||||
if len(authorizedActions) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId)
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(item, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filterable, err := subtypes.Filterable(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if filter.Match(filterable) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
}
|
||||
return &pbs.ListCredentialsResponse{Items: finalItems}, nil
|
||||
}
|
||||
|
||||
// GetCredential implements the interface pbs.CredentialServiceServer.
|
||||
func (s Service) GetCredential(ctx context.Context, req *pbs.GetCredentialRequest) (*pbs.GetCredentialResponse, error) {
|
||||
const op = "credentials.(Service).GetCredential"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Read)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
c, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, c.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(c, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.GetCredentialResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateCredential implements the interface pbs.CredentialServiceServer.
|
||||
func (s Service) CreateCredential(ctx context.Context, req *pbs.CreateCredentialRequest) (*pbs.CreateCredentialResponse, error) {
|
||||
const op = "credentials.(Service).CreateCredential"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetItem().GetCredentialStoreId(), action.Create)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
cl, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cl.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(cl, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.CreateCredentialResponse{
|
||||
Item: item,
|
||||
Uri: fmt.Sprintf("credentials/%s", item.GetId()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateCredential implements the interface pbs.CredentialServiceServer.
|
||||
func (s Service) UpdateCredential(ctx context.Context, req *pbs.UpdateCredentialRequest) (*pbs.UpdateCredentialResponse, error) {
|
||||
const op = "credentials.(Service).UpdateCredential"
|
||||
|
||||
cur, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storeId := cur.GetStoreId()
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Update)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
c, err := s.updateInRepo(ctx, authResults.Scope.GetId(), storeId, req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, c.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(c, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.UpdateCredentialResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteCredential implements the interface pbs.CredentialServiceServer.
|
||||
func (s Service) DeleteCredential(ctx context.Context, req *pbs.DeleteCredentialRequest) (*pbs.DeleteCredentialResponse, error) {
|
||||
if err := validateDeleteRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.Delete)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
_, err := s.deleteFromRepo(ctx, authResults.Scope.GetId(), req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, storeId string) ([]credential.Static, error) {
|
||||
const op = "credentials.(Service).listFromRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
up, err := repo.ListCredentials(ctx, storeId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
creds := make([]credential.Static, 0, len(up))
|
||||
for _, s := range up {
|
||||
creds = append(creds, s)
|
||||
}
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (credential.Static, error) {
|
||||
const op = "credentials.(Service).getFromRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cred, err := repo.LookupCredential(ctx, id)
|
||||
if err != nil && !errors.IsNotFoundError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if cred == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("credential %q not found", id))
|
||||
}
|
||||
return cred, err
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Credential) (credential.Static, error) {
|
||||
const op = "credentials.(Service).createInRepo"
|
||||
switch item.GetType() {
|
||||
case static.UsernamePasswordSubtype.String():
|
||||
cred, err := toUsernamePasswordStorageCredential(item.GetCredentialStoreId(), item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, err := repo.CreateUsernamePasswordCredential(ctx, scopeId, cred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential but no error returned from repository.")
|
||||
}
|
||||
return out, nil
|
||||
default:
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, fmt.Sprintf("Unsupported credential type %q", item.GetType()))
|
||||
}
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(
|
||||
ctx context.Context,
|
||||
scopeId, storeId, id string,
|
||||
masks []string,
|
||||
in *pb.Credential,
|
||||
) (credential.Static, error) {
|
||||
const op = "credentials.(Service).updateInRepo"
|
||||
|
||||
var dbMasks []string
|
||||
item := proto.Clone(in).(*pb.Credential)
|
||||
|
||||
dbMasks = append(dbMasks, maskManager.Translate(masks)...)
|
||||
if len(dbMasks) == 0 {
|
||||
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
}
|
||||
|
||||
switch subtypes.SubtypeFromId(domain, id) {
|
||||
case static.UsernamePasswordSubtype:
|
||||
cred, err := toUsernamePasswordStorageCredential(storeId, in)
|
||||
cred.PublicId = id
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, rowsUpdated, err := repo.UpdateUsernamePasswordCredential(ctx, scopeId, cred, item.GetVersion(), dbMasks)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential"))
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Credential %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return out, nil
|
||||
default:
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, fmt.Sprintf("Unsupported credential type %q", item.GetType()))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) {
|
||||
const op = "credentials.(Service).deleteFromRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
rows, err := repo.DeleteCredential(ctx, scopeId, id)
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential"))
|
||||
}
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
const op = "credentials.(Service).authResult"
|
||||
res := auth.VerifyResults{}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
}
|
||||
|
||||
var parentId string
|
||||
opts := []auth.Option{auth.WithType(resource.Credential), auth.WithAction(a)}
|
||||
switch a {
|
||||
case action.List, action.Create:
|
||||
parentId = id
|
||||
default:
|
||||
opts = append(opts, auth.WithId(id))
|
||||
cred, err := repo.LookupCredential(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
}
|
||||
if cred == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return res
|
||||
}
|
||||
parentId = cred.GetStoreId()
|
||||
|
||||
}
|
||||
|
||||
if parentId == "" {
|
||||
res.Error = errors.New(ctx, errors.RecordNotFound, op, "unable to find credential store for provided credential")
|
||||
return res
|
||||
}
|
||||
opts = append(opts, auth.WithPin(parentId))
|
||||
|
||||
cs, err := repo.LookupCredentialStore(ctx, parentId)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
}
|
||||
if cs == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return res
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(cs.GetScopeId()))
|
||||
|
||||
return auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(in credential.Static, opt ...handlers.Option) (*pb.Credential, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building credential proto")
|
||||
}
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.Credential{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if outputFields.Has(globals.CredentialStoreIdField) {
|
||||
out.CredentialStoreId = in.GetStoreId()
|
||||
}
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
switch in.(type) {
|
||||
case *static.UsernamePasswordCredential:
|
||||
out.Type = static.UsernamePasswordSubtype.String()
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = wrapperspb.String(in.GetDescription())
|
||||
}
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = wrapperspb.String(in.GetName())
|
||||
}
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
|
||||
switch cred := in.(type) {
|
||||
case *static.UsernamePasswordCredential:
|
||||
if outputFields.Has(globals.AttributesField) {
|
||||
out.Attrs = &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String(cred.GetUsername()),
|
||||
PasswordHmac: base64.RawURLEncoding.EncodeToString(cred.GetPasswordHmac()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func toUsernamePasswordStorageCredential(storeId string, in *pb.Credential) (out *static.UsernamePasswordCredential, err error) {
|
||||
const op = "credentials.toStorageCredential"
|
||||
var opts []static.Option
|
||||
if in.GetName() != nil {
|
||||
opts = append(opts, static.WithName(in.GetName().GetValue()))
|
||||
}
|
||||
if in.GetDescription() != nil {
|
||||
opts = append(opts, static.WithDescription(in.GetDescription().GetValue()))
|
||||
}
|
||||
|
||||
attrs := in.GetUsernamePasswordAttributes()
|
||||
cs, err := static.NewUsernamePasswordCredential(
|
||||
storeId,
|
||||
attrs.GetUsername().GetValue(),
|
||||
credential.Password(attrs.GetPassword().GetValue()),
|
||||
opts...)
|
||||
if err != nil {
|
||||
return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to build credential"))
|
||||
}
|
||||
|
||||
return cs, err
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
// requirements on the structure of the request. They verify that:
|
||||
// * The path passed in is correctly formatted
|
||||
// * All required parameters are set
|
||||
// * There are no conflicting parameters provided
|
||||
func validateGetRequest(req *pbs.GetCredentialRequest) error {
|
||||
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.CredentialPrefix)
|
||||
}
|
||||
|
||||
func validateCreateRequest(req *pbs.CreateCredentialRequest) error {
|
||||
return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string {
|
||||
badFields := map[string]string{}
|
||||
if req.Item.GetType() != static.UsernamePasswordSubtype.String() {
|
||||
badFields[globals.TypeField] = fmt.Sprintf("Unsupported credential type %q", req.Item.GetType())
|
||||
}
|
||||
if req.Item.GetCredentialStoreId() == "" {
|
||||
badFields[globals.CredentialStoreIdField] = "This field must be a valid credential store id."
|
||||
}
|
||||
|
||||
if req.Item.GetUsernamePasswordAttributes().GetUsername().GetValue() == "" {
|
||||
badFields[usernameField] = "Field required for creating a username-password credential."
|
||||
}
|
||||
if req.Item.GetUsernamePasswordAttributes().GetPassword().GetValue() == "" {
|
||||
badFields[passwordField] = "Field required for creating a username-password credential."
|
||||
}
|
||||
|
||||
return badFields
|
||||
})
|
||||
}
|
||||
|
||||
func validateUpdateRequest(req *pbs.UpdateCredentialRequest) error {
|
||||
return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
|
||||
badFields := map[string]string{}
|
||||
if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.UsernamePasswordSubtype.String() {
|
||||
badFields[globals.TypeField] = "Cannot modify resource type."
|
||||
}
|
||||
|
||||
attrs := req.GetItem().GetUsernamePasswordAttributes()
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), usernameField) && attrs.GetUsername().GetValue() == "" {
|
||||
badFields[usernameField] = "This is a required field and cannot be set to empty."
|
||||
}
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), passwordField) && attrs.GetPassword().GetValue() == "" {
|
||||
badFields[passwordField] = "This is a required field and cannot be set to empty."
|
||||
}
|
||||
|
||||
return badFields
|
||||
}, static.CredentialPrefix)
|
||||
}
|
||||
|
||||
func validateDeleteRequest(req *pbs.DeleteCredentialRequest) error {
|
||||
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.CredentialPrefix)
|
||||
}
|
||||
|
||||
func validateListRequest(req *pbs.ListCredentialsRequest) error {
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(handlers.Id(req.GetCredentialStoreId()), static.CredentialStorePrefix) {
|
||||
badFields[globals.CredentialStoreIdField] = "This field must be a valid credential store id."
|
||||
}
|
||||
if _, err := handlers.NewFilter(req.GetFilter()); err != nil {
|
||||
badFields["filter"] = fmt.Sprintf("This field could not be parsed. %v", err)
|
||||
}
|
||||
if len(badFields) > 0 {
|
||||
return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,742 @@
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/libs/crypto"
|
||||
"github.com/hashicorp/boundary/internal/scheduler"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
|
||||
scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
var testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kkms := kms.TestKms(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
staticRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(context.Background(), rw, rw, kkms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
storeNoCreds := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
|
||||
var wantCreds []*pb.Credential
|
||||
for i := 0; i < 10; i++ {
|
||||
user := fmt.Sprintf("user-%d", i)
|
||||
pass := fmt.Sprintf("pass-%d", i)
|
||||
c := static.TestUsernamePasswordCredential(t, conn, wrapper, user, pass, store.GetPublicId(), prj.GetPublicId())
|
||||
|
||||
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase)
|
||||
require.NoError(t, err)
|
||||
hm, err := crypto.HmacSha256(context.Background(), []byte(pass), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(t, err)
|
||||
|
||||
wantCreds = append(wantCreds, &pb.Credential{
|
||||
Id: c.GetPublicId(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
CreatedTime: c.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: c.GetUpdateTime().GetTimestamp(),
|
||||
Version: c.GetVersion(),
|
||||
Type: static.UsernamePasswordSubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String(c.GetUsername()),
|
||||
PasswordHmac: base64.RawURLEncoding.EncodeToString([]byte(hm)),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.ListCredentialsRequest
|
||||
res *pbs.ListCredentialsResponse
|
||||
anonRes *pbs.ListCredentialsResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "List many credentials",
|
||||
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId()},
|
||||
res: &pbs.ListCredentialsResponse{Items: wantCreds},
|
||||
anonRes: &pbs.ListCredentialsResponse{Items: wantCreds},
|
||||
},
|
||||
{
|
||||
name: "List no credentials",
|
||||
req: &pbs.ListCredentialsRequest{CredentialStoreId: storeNoCreds.GetPublicId()},
|
||||
res: &pbs.ListCredentialsResponse{},
|
||||
anonRes: &pbs.ListCredentialsResponse{},
|
||||
},
|
||||
{
|
||||
name: "Filter to one credential",
|
||||
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantCreds[1].GetId())},
|
||||
res: &pbs.ListCredentialsResponse{Items: wantCreds[1:2]},
|
||||
anonRes: &pbs.ListCredentialsResponse{Items: wantCreds[1:2]},
|
||||
},
|
||||
{
|
||||
name: "Filter on Attribute",
|
||||
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/username"==%q`, wantCreds[2].GetUsernamePasswordAttributes().GetUsername().Value)},
|
||||
res: &pbs.ListCredentialsResponse{Items: wantCreds[2:3]},
|
||||
anonRes: &pbs.ListCredentialsResponse{}, // anonymous user does not have access to attributes
|
||||
},
|
||||
{
|
||||
name: "Filter to no credential",
|
||||
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
|
||||
res: &pbs.ListCredentialsResponse{},
|
||||
anonRes: &pbs.ListCredentialsResponse{},
|
||||
},
|
||||
{
|
||||
name: "Filter Bad Format",
|
||||
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: `"//id/"=="bad"`},
|
||||
err: handlers.InvalidArgumentErrorf("bad format", nil),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s, err := NewService(staticRepoFn, iamRepoFn)
|
||||
require.NoError(t, err, "Couldn't create new host set service.")
|
||||
|
||||
// Test non-anonymous listing
|
||||
got, gErr := s.ListCredentials(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(t, gErr)
|
||||
assert.True(t, errors.Is(gErr, tc.err), "ListCredentialStore(%q) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, gErr)
|
||||
assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform(), protocmp.SortRepeated(func(x, y *pb.Credential) bool {
|
||||
return x.Id < y.Id
|
||||
})))
|
||||
|
||||
// Test anonymous listing
|
||||
got, gErr = s.ListCredentials(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
require.NoError(t, gErr)
|
||||
assert.Len(t, got.Items, len(tc.anonRes.Items))
|
||||
for _, item := range got.GetItems() {
|
||||
require.Nil(t, item.CreatedTime)
|
||||
require.Nil(t, item.UpdatedTime)
|
||||
require.Zero(t, item.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kkms := kms.TestKms(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
staticRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(context.Background(), rw, rw, kkms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
cred := static.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId())
|
||||
s, err := NewService(staticRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Calculate hmac
|
||||
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase)
|
||||
require.NoError(t, err)
|
||||
hm, err := crypto.HmacSha256(context.Background(), []byte("pass"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
id string
|
||||
res *pbs.GetCredentialResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
id: cred.GetPublicId(),
|
||||
res: &pbs.GetCredentialResponse{
|
||||
Item: &pb.Credential{
|
||||
Id: cred.GetPublicId(),
|
||||
CredentialStoreId: cred.GetStoreId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: store.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
Type: static.UsernamePasswordSubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
CreatedTime: cred.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: cred.UpdateTime.GetTimestamp(),
|
||||
Version: 1,
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("user"),
|
||||
PasswordHmac: base64.RawURLEncoding.EncodeToString([]byte(hm)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", static.CredentialPrefix),
|
||||
err: handlers.NotFoundError(),
|
||||
},
|
||||
{
|
||||
name: "bad prefix",
|
||||
id: fmt.Sprintf("%s_1234567890", static.CredentialStorePrefix),
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
req := &pbs.GetCredentialRequest{Id: tc.id}
|
||||
// Test non-anonymous get
|
||||
got, gErr := s.GetCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), req)
|
||||
if tc.err != nil {
|
||||
require.Error(t, gErr)
|
||||
assert.True(t, errors.Is(gErr, tc.err))
|
||||
return
|
||||
}
|
||||
require.NoError(t, gErr)
|
||||
assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform()))
|
||||
|
||||
// Test anonymous get
|
||||
got, gErr = s.GetCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), req)
|
||||
require.NoError(t, gErr)
|
||||
require.Nil(t, got.Item.CreatedTime)
|
||||
require.Nil(t, got.Item.UpdatedTime)
|
||||
require.Zero(t, got.Item.Version)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
staticRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(context.Background(), rw, rw, kms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
cred := static.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId())
|
||||
s, err := NewService(staticRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
id string
|
||||
err error
|
||||
res *pbs.DeleteCredentialResponse
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
id: cred.GetPublicId(),
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", static.CredentialPrefix),
|
||||
err: handlers.NotFoundError(),
|
||||
},
|
||||
{
|
||||
name: "bad prefix",
|
||||
id: fmt.Sprintf("%s_1234567890", static.CredentialStorePrefix),
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, gErr := s.DeleteCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), &pbs.DeleteCredentialRequest{Id: tc.id})
|
||||
assert.EqualValuesf(t, tc.res, got, "DeleteCredentialStore got response %q, wanted %q", got, tc.res)
|
||||
if tc.err != nil {
|
||||
require.Error(t, gErr)
|
||||
assert.True(t, errors.Is(gErr, tc.err))
|
||||
return
|
||||
}
|
||||
require.NoError(t, gErr)
|
||||
g, err := s.GetCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), &pbs.GetCredentialRequest{Id: tc.id})
|
||||
assert.Nil(t, g)
|
||||
assert.True(t, errors.Is(err, handlers.NotFoundError()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kkms := kms.TestKms(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
repoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(context.Background(), rw, rw, kkms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.CreateCredentialRequest
|
||||
res *pbs.CreateCredentialResponse
|
||||
idPrefix string
|
||||
err error
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Can't specify Id",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Id: static.CredentialPrefix + "_notallowed",
|
||||
Type: static.UsernamePasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("username"),
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Can't specify Created Time",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: timestamppb.Now(),
|
||||
Type: static.UsernamePasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("username"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Can't specify Updated Time",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
UpdatedTime: timestamppb.Now(),
|
||||
Type: static.UsernamePasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("username"),
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Must provide type",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("username"),
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Must provide username",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: static.UsernamePasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Must provide password]",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: static.UsernamePasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("username"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
req: &pbs.CreateCredentialRequest{Item: &pb.Credential{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: static.UsernamePasswordSubtype.String(),
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("username"),
|
||||
Password: wrapperspb.String("password"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
idPrefix: static.CredentialPrefix + "_",
|
||||
res: &pbs.CreateCredentialResponse{
|
||||
Uri: fmt.Sprintf("credentials/%s_", static.CredentialPrefix),
|
||||
Item: &pb.Credential{
|
||||
Id: store.GetPublicId(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CreatedTime: store.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: store.GetUpdateTime().GetTimestamp(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: static.UsernamePasswordSubtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
s, err := NewService(repoFn, iamRepoFn)
|
||||
require.NoError(err, "Error when getting new credential store service.")
|
||||
|
||||
got, gErr := s.CreateCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req)
|
||||
if tc.wantErr || tc.err != nil {
|
||||
require.Error(gErr)
|
||||
if tc.err != nil {
|
||||
assert.True(errors.Is(gErr, tc.err), "CreateCredential(...) got error %v, wanted %v", gErr, tc.err)
|
||||
}
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
if tc.res == nil {
|
||||
require.Nil(got)
|
||||
}
|
||||
if got != nil {
|
||||
assert.Contains(got.GetUri(), tc.res.Uri)
|
||||
assert.True(strings.HasPrefix(got.GetItem().GetId(), tc.idPrefix))
|
||||
gotCreateTime := got.GetItem().GetCreatedTime()
|
||||
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
||||
|
||||
// Verify it is a credential store created after the test setup's default credential store
|
||||
assert.True(gotCreateTime.AsTime().After(store.CreateTime.AsTime()), "New credential should have been created after default credential store. Was created %v, which is after %v", gotCreateTime, store.CreateTime.AsTime())
|
||||
assert.True(gotUpdateTime.AsTime().After(store.CreateTime.AsTime()), "New credential should have been updated after default credential store. Was updated %v, which is after %v", gotUpdateTime, store.CreateTime.AsTime())
|
||||
|
||||
// Calculate hmac
|
||||
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.PublicId, kms.KeyPurposeDatabase)
|
||||
password := tc.req.GetItem().GetUsernamePasswordAttributes().GetPassword().GetValue()
|
||||
hm, err := crypto.HmacSha256(context.Background(), []byte(password), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(err)
|
||||
|
||||
// Validate attributes equal
|
||||
assert.Equal(tc.req.GetItem().GetUsernamePasswordAttributes().GetUsername().GetValue(),
|
||||
got.GetItem().GetUsernamePasswordAttributes().GetUsername().GetValue())
|
||||
assert.Equal(base64.RawURLEncoding.EncodeToString([]byte(hm)), got.GetItem().GetUsernamePasswordAttributes().GetPasswordHmac())
|
||||
assert.Empty(got.GetItem().GetUsernamePasswordAttributes().GetPassword())
|
||||
|
||||
// Clear attributes for compare below
|
||||
got.Item.Attrs = nil
|
||||
|
||||
// Clear all values which are hard to compare against.
|
||||
got.Uri, tc.res.Uri = "", ""
|
||||
got.Item.Id, tc.res.Item.Id = "", ""
|
||||
got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform(), protocmp.SortRepeatedFields(got)), "CreateCredential(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kkms := kms.TestKms(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
staticRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(context.Background(), rw, rw, kkms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
|
||||
|
||||
s, err := NewService(staticRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
|
||||
return &fieldmaskpb.FieldMask{Paths: paths}
|
||||
}
|
||||
|
||||
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
|
||||
freshCred := func(user, pass string) (*static.UsernamePasswordCredential, func()) {
|
||||
t.Helper()
|
||||
cred := static.TestUsernamePasswordCredential(t, conn, wrapper, user, pass, store.GetPublicId(), prj.GetPublicId())
|
||||
clean := func() {
|
||||
_, err := s.DeleteCredential(ctx, &pbs.DeleteCredentialRequest{Id: cred.GetPublicId()})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return cred, clean
|
||||
}
|
||||
|
||||
databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase)
|
||||
require.NoError(t, err)
|
||||
|
||||
successCases := []struct {
|
||||
name string
|
||||
req *pbs.UpdateCredentialRequest
|
||||
res func(cred *pb.Credential) *pb.Credential
|
||||
}{
|
||||
{
|
||||
name: "name",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("name"),
|
||||
Item: &pb.Credential{
|
||||
Name: wrapperspb.String("new-name"),
|
||||
},
|
||||
},
|
||||
res: func(in *pb.Credential) *pb.Credential {
|
||||
out := proto.Clone(in).(*pb.Credential)
|
||||
out.Name = wrapperspb.String("new-name")
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("description"),
|
||||
Item: &pb.Credential{
|
||||
Description: wrapperspb.String("new-description"),
|
||||
},
|
||||
},
|
||||
res: func(in *pb.Credential) *pb.Credential {
|
||||
out := proto.Clone(in).(*pb.Credential)
|
||||
out.Description = wrapperspb.String("new-description")
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "name-and-description",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("name", "description"),
|
||||
Item: &pb.Credential{
|
||||
Name: wrapperspb.String("new-name"),
|
||||
Description: wrapperspb.String("new-description"),
|
||||
},
|
||||
},
|
||||
res: func(in *pb.Credential) *pb.Credential {
|
||||
out := proto.Clone(in).(*pb.Credential)
|
||||
out.Name = wrapperspb.String("new-name")
|
||||
out.Description = wrapperspb.String("new-description")
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "update-username",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("attributes.username"),
|
||||
Item: &pb.Credential{
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("new-user-name"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: func(in *pb.Credential) *pb.Credential {
|
||||
out := proto.Clone(in).(*pb.Credential)
|
||||
out.GetUsernamePasswordAttributes().Username = wrapperspb.String("new-user-name")
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "update-password",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("attributes.password"),
|
||||
Item: &pb.Credential{
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Password: wrapperspb.String("new-password"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: func(in *pb.Credential) *pb.Credential {
|
||||
out := proto.Clone(in).(*pb.Credential)
|
||||
hm, err := crypto.HmacSha256(context.Background(), []byte("new-password"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(t, err)
|
||||
out.GetUsernamePasswordAttributes().PasswordHmac = base64.RawURLEncoding.EncodeToString([]byte(hm))
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "update-username-and-password",
|
||||
req: &pbs.UpdateCredentialRequest{
|
||||
UpdateMask: fieldmask("attributes.username", "attributes.password"),
|
||||
Item: &pb.Credential{
|
||||
Attrs: &pb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("new-username"),
|
||||
Password: wrapperspb.String("new-password"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: func(in *pb.Credential) *pb.Credential {
|
||||
out := proto.Clone(in).(*pb.Credential)
|
||||
|
||||
out.GetUsernamePasswordAttributes().Username = wrapperspb.String("new-username")
|
||||
|
||||
hm, err := crypto.HmacSha256(context.Background(), []byte("new-password"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519())
|
||||
require.NoError(t, err)
|
||||
out.GetUsernamePasswordAttributes().PasswordHmac = base64.RawURLEncoding.EncodeToString([]byte(hm))
|
||||
return out
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range successCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
cred, cleanup := freshCred("user", "pass")
|
||||
defer cleanup()
|
||||
|
||||
if tc.req.Item.GetVersion() == 0 {
|
||||
tc.req.Item.Version = 1
|
||||
}
|
||||
if tc.req.GetId() == "" {
|
||||
tc.req.Id = cred.GetPublicId()
|
||||
}
|
||||
resToChange, err := s.GetCredential(ctx, &pbs.GetCredentialRequest{Id: cred.GetPublicId()})
|
||||
require.NoError(err)
|
||||
want := &pbs.UpdateCredentialResponse{Item: tc.res(resToChange.GetItem())}
|
||||
|
||||
got, gErr := s.UpdateCredential(ctx, tc.req)
|
||||
require.NoError(gErr)
|
||||
require.NotNil(got)
|
||||
|
||||
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
||||
created := cred.GetCreateTime().GetTimestamp()
|
||||
assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
|
||||
|
||||
want.Item.UpdatedTime = got.Item.UpdatedTime
|
||||
|
||||
assert.EqualValues(2, got.Item.Version)
|
||||
want.Item.Version = 2
|
||||
|
||||
assert.Empty(cmp.Diff(got, want, protocmp.Transform()))
|
||||
})
|
||||
}
|
||||
|
||||
// cant update read only fields
|
||||
cred, cleanup := freshCred("user", "pass")
|
||||
defer cleanup()
|
||||
|
||||
newStore := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
|
||||
roCases := []struct {
|
||||
path string
|
||||
item *pb.Credential
|
||||
matcher func(t *testing.T, e error) // When not set defaults to checking against InvalidArgument Error
|
||||
}{
|
||||
{
|
||||
path: "type",
|
||||
item: &pb.Credential{Type: "something"},
|
||||
},
|
||||
{
|
||||
path: "store_id",
|
||||
item: &pb.Credential{CredentialStoreId: newStore.GetPublicId()},
|
||||
},
|
||||
{
|
||||
path: "updated_time",
|
||||
item: &pb.Credential{UpdatedTime: timestamppb.Now()},
|
||||
},
|
||||
{
|
||||
path: "created_time",
|
||||
item: &pb.Credential{UpdatedTime: timestamppb.Now()},
|
||||
},
|
||||
{
|
||||
path: "authorized_actions",
|
||||
item: &pb.Credential{AuthorizedActions: append(testAuthorizedActions, "another")},
|
||||
},
|
||||
}
|
||||
for _, tc := range roCases {
|
||||
t.Run(fmt.Sprintf("ReadOnlyField/%s", tc.path), func(t *testing.T) {
|
||||
req := &pbs.UpdateCredentialRequest{
|
||||
Id: cred.GetPublicId(),
|
||||
Item: tc.item,
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{tc.path}},
|
||||
}
|
||||
req.Item.Version = cred.Version
|
||||
|
||||
got, gErr := s.UpdateCredential(ctx, req)
|
||||
assert.Error(t, gErr)
|
||||
matcher := tc.matcher
|
||||
if matcher == nil {
|
||||
matcher = func(t *testing.T, e error) {
|
||||
assert.Truef(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)), "got error %v, wanted invalid argument", gErr)
|
||||
}
|
||||
}
|
||||
matcher(t, gErr)
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault/store"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentials"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
@@ -25,6 +27,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentialstores"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
@@ -57,9 +60,12 @@ var (
|
||||
action.List,
|
||||
}
|
||||
|
||||
collectionTypeMap = map[resource.Type]action.ActionSet{
|
||||
vaultCollectionTypeMap = map[resource.Type]action.ActionSet{
|
||||
resource.CredentialLibrary: credentiallibraries.CollectionActions,
|
||||
}
|
||||
staticCollectionTypeMap = map[resource.Type]action.ActionSet{
|
||||
resource.Credential: credentials.CollectionActions,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -74,20 +80,28 @@ func init() {
|
||||
type Service struct {
|
||||
pbs.UnimplementedCredentialStoreServiceServer
|
||||
|
||||
iamRepoFn common.IamRepoFactory
|
||||
repoFn common.VaultCredentialRepoFactory
|
||||
iamRepoFn common.IamRepoFactory
|
||||
vaultRepoFn common.VaultCredentialRepoFactory
|
||||
staticRepoFn common.StaticCredentialRepoFactory
|
||||
}
|
||||
|
||||
// NewService returns a credential store service which handles credential store related requests to boundary.
|
||||
func NewService(repo common.VaultCredentialRepoFactory, iamRepo common.IamRepoFactory) (Service, error) {
|
||||
func NewService(
|
||||
vaultRepo common.VaultCredentialRepoFactory,
|
||||
staticRepo common.StaticCredentialRepoFactory,
|
||||
iamRepo common.IamRepoFactory,
|
||||
) (Service, error) {
|
||||
const op = "credentialstores.NewService"
|
||||
if iamRepo == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing iam repository")
|
||||
}
|
||||
if repo == nil {
|
||||
if vaultRepo == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing vault credential repository")
|
||||
}
|
||||
return Service{iamRepoFn: iamRepo, repoFn: repo}, nil
|
||||
if staticRepo == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static credential repository")
|
||||
}
|
||||
return Service{iamRepoFn: iamRepo, vaultRepoFn: vaultRepo, staticRepoFn: staticRepo}, nil
|
||||
}
|
||||
|
||||
var _ pbs.CredentialStoreServiceServer = Service{}
|
||||
@@ -155,7 +169,7 @@ func (s Service) ListCredentialStores(ctx context.Context, req *pbs.ListCredenti
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, item.GetPublicId())
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, item.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -208,7 +222,7 @@ func (s Service) GetCredentialStore(ctx context.Context, req *pbs.GetCredentialS
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, cs.GetPublicId())
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -253,7 +267,7 @@ func (s Service) CreateCredentialStore(ctx context.Context, req *pbs.CreateCrede
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, cs.GetPublicId())
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -301,7 +315,7 @@ func (s Service) UpdateCredentialStore(ctx context.Context, req *pbs.UpdateCrede
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, cs.GetPublicId())
|
||||
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -332,74 +346,154 @@ func (s Service) DeleteCredentialStore(ctx context.Context, req *pbs.DeleteCrede
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*vault.CredentialStore, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]credential.Store, error) {
|
||||
const op = "credentialstores.(Service).listFromRepo"
|
||||
repo, err := s.repoFn()
|
||||
|
||||
vaultRepo, err := s.vaultRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
csl, err := repo.ListCredentialStores(ctx, scopeIds)
|
||||
vaultCsl, err := vaultRepo.ListCredentialStores(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
staticRepo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
staticCsl, err := staticRepo.ListCredentialStores(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
csl := make([]credential.Store, 0, len(staticCsl)+len(vaultCsl))
|
||||
for _, s := range vaultCsl {
|
||||
csl = append(csl, s)
|
||||
}
|
||||
for _, s := range staticCsl {
|
||||
csl = append(csl, s)
|
||||
}
|
||||
|
||||
return csl, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (credential.Store, error) {
|
||||
const op = "credentialstores.(Service).getFromRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
|
||||
switch subtypes.SubtypeFromId(domain, id) {
|
||||
case vault.Subtype:
|
||||
repo, err := s.vaultRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cs, err := repo.LookupCredentialStore(ctx, id)
|
||||
if err != nil && !errors.IsNotFoundError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if cs != nil {
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
case static.Subtype:
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cs, err := repo.LookupCredentialStore(ctx, id)
|
||||
if err != nil && !errors.IsNotFoundError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if cs != nil {
|
||||
return cs, nil
|
||||
}
|
||||
}
|
||||
cs, err := repo.LookupCredentialStore(ctx, id)
|
||||
if err != nil && !errors.IsNotFoundError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if cs == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("credential store %q not found", id))
|
||||
}
|
||||
return cs, err
|
||||
|
||||
return nil, handlers.NotFoundErrorf("credential store %q not found", id)
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, projId string, item *pb.CredentialStore) (credential.Store, error) {
|
||||
const op = "credentialstores.(Service).createInRepo"
|
||||
cs, err := toStorageVaultStore(projId, item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
|
||||
switch item.Type {
|
||||
case vault.Subtype.String():
|
||||
cs, err := toStorageVaultStore(projId, item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
repo, err := s.vaultRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, err := repo.CreateCredentialStore(ctx, cs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential store"))
|
||||
}
|
||||
return out, nil
|
||||
|
||||
case static.Subtype.String():
|
||||
cs, err := toStorageStaticStore(projId, item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, err := repo.CreateCredentialStore(ctx, cs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential store"))
|
||||
}
|
||||
return out, nil
|
||||
|
||||
default:
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential store, unknown type.")
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, err := repo.CreateCredentialStore(ctx, cs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential store"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential store but no error returned from repository.")
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []string, item *pb.CredentialStore) (credential.Store, error) {
|
||||
const op = "credentialstores.(Service).updateInRepo"
|
||||
cs, err := toStorageVaultStore(projId, item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cs.PublicId = id
|
||||
|
||||
var out credential.Store
|
||||
var rowsUpdated int
|
||||
|
||||
dbMask := maskManager.Translate(mask)
|
||||
if len(dbMask) == 0 {
|
||||
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, rowsUpdated, err := repo.UpdateCredentialStore(ctx, cs, item.GetVersion(), dbMask)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential store"))
|
||||
|
||||
switch subtypes.SubtypeFromId(domain, id) {
|
||||
case vault.Subtype:
|
||||
cs, err := toStorageVaultStore(projId, item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cs.PublicId = id
|
||||
|
||||
repo, err := s.vaultRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, rowsUpdated, err = repo.UpdateCredentialStore(ctx, cs, item.GetVersion(), dbMask)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential store"))
|
||||
}
|
||||
|
||||
case static.Subtype:
|
||||
cs, err := toStorageStaticStore(projId, item)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cs.PublicId = id
|
||||
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
out, rowsUpdated, err = repo.UpdateCredentialStore(ctx, cs, item.GetVersion(), dbMask)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential store"))
|
||||
}
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Credential Store %q doesn't exist or incorrect version provided.", id)
|
||||
@@ -409,16 +503,34 @@ func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []str
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
const op = "credentialstores.(Service).deleteFromRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
rows, err := repo.DeleteCredentialStore(ctx, id)
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return false, nil
|
||||
var rows int
|
||||
|
||||
switch subtypes.SubtypeFromId(domain, id) {
|
||||
case vault.Subtype:
|
||||
repo, err := s.vaultRepoFn()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
rows, err = repo.DeleteCredentialStore(ctx, id)
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential store"))
|
||||
}
|
||||
|
||||
case static.Subtype:
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
rows, err = repo.DeleteCredentialStore(ctx, id)
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential store"))
|
||||
}
|
||||
return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential store"))
|
||||
}
|
||||
return rows > 0, nil
|
||||
}
|
||||
@@ -430,7 +542,12 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
res.Error = err
|
||||
return res
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
vaultRepo, err := s.vaultRepoFn()
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
}
|
||||
staticRepo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
@@ -451,16 +568,32 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return res
|
||||
}
|
||||
default:
|
||||
cs, err := repo.LookupCredentialStore(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
switch subtypes.SubtypeFromId(domain, id) {
|
||||
case vault.Subtype:
|
||||
cs, err := vaultRepo.LookupCredentialStore(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
}
|
||||
if cs == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return res
|
||||
}
|
||||
parentId = cs.GetScopeId()
|
||||
|
||||
case static.Subtype:
|
||||
var err error
|
||||
cs, err := staticRepo.LookupCredentialStore(ctx, id)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
}
|
||||
if cs == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return res
|
||||
}
|
||||
parentId = cs.GetScopeId()
|
||||
}
|
||||
if cs == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return res
|
||||
}
|
||||
parentId = cs.GetScopeId()
|
||||
opts = append(opts, auth.WithId(id))
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
@@ -550,6 +683,23 @@ func toProto(in credential.Store, opt ...handlers.Option) (*pb.CredentialStore,
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func toStorageStaticStore(scopeId string, in *pb.CredentialStore) (out *static.CredentialStore, err error) {
|
||||
const op = "credentialstores.toStorageStaticStore"
|
||||
var opts []static.Option
|
||||
if in.GetName() != nil {
|
||||
opts = append(opts, static.WithName(in.GetName().GetValue()))
|
||||
}
|
||||
if in.GetDescription() != nil {
|
||||
opts = append(opts, static.WithDescription(in.GetDescription().GetValue()))
|
||||
}
|
||||
|
||||
cs, err := static.NewCredentialStore(scopeId, opts...)
|
||||
if err != nil {
|
||||
return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to build credential store for creation"))
|
||||
}
|
||||
return cs, err
|
||||
}
|
||||
|
||||
func toStorageVaultStore(scopeId string, in *pb.CredentialStore) (out *vault.CredentialStore, err error) {
|
||||
const op = "credentialstores.toStorageVaultStore"
|
||||
var opts []vault.Option
|
||||
@@ -609,7 +759,7 @@ func toStorageVaultStore(scopeId string, in *pb.CredentialStore) (out *vault.Cre
|
||||
// * All required parameters are set
|
||||
// * There are no conflicting parameters provided
|
||||
func validateGetRequest(req *pbs.GetCredentialStoreRequest) error {
|
||||
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix)
|
||||
return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix, static.CredentialStorePrefix)
|
||||
}
|
||||
|
||||
func validateCreateRequest(req *pbs.CreateCredentialStoreRequest) error {
|
||||
@@ -651,6 +801,8 @@ func validateCreateRequest(req *pbs.CreateCredentialStoreRequest) error {
|
||||
if len(cs) > 0 && pk == nil {
|
||||
badFields[clientCertField] = "Cannot set a client certificate without a private key."
|
||||
}
|
||||
case static.Subtype:
|
||||
// No additional validation required for static credential store
|
||||
default:
|
||||
badFields[globals.TypeField] = "This is a required field and must be a known credential store type."
|
||||
}
|
||||
@@ -693,11 +845,11 @@ func validateUpdateRequest(req *pbs.UpdateCredentialStoreRequest) error {
|
||||
}
|
||||
}
|
||||
return badFields
|
||||
}, vault.CredentialStorePrefix)
|
||||
}, vault.CredentialStorePrefix, static.CredentialStorePrefix)
|
||||
}
|
||||
|
||||
func validateDeleteRequest(req *pbs.DeleteCredentialStoreRequest) error {
|
||||
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix)
|
||||
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix, static.CredentialStorePrefix)
|
||||
}
|
||||
|
||||
func validateListRequest(req *pbs.ListCredentialStoresRequest) error {
|
||||
@@ -714,3 +866,20 @@ func validateListRequest(req *pbs.ListCredentialStoresRequest) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func calculateAuthorizedCollectionActions(ctx context.Context, authResults auth.VerifyResults, id string) (map[string]*structpb.ListValue, error) {
|
||||
var collectionActions map[string]*structpb.ListValue
|
||||
var err error
|
||||
switch subtypes.SubtypeFromId(domain, id) {
|
||||
case vault.Subtype:
|
||||
collectionActions, err = auth.CalculateAuthorizedCollectionActions(ctx, authResults, vaultCollectionTypeMap, authResults.Scope.Id, id)
|
||||
|
||||
case static.Subtype:
|
||||
collectionActions, err = auth.CalculateAuthorizedCollectionActions(ctx, authResults, staticCollectionTypeMap, authResults.Scope.Id, id)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return collectionActions, nil
|
||||
}
|
||||
+453
-52
@@ -11,6 +11,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
credstatic "github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
@@ -36,8 +37,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
|
||||
testAuthorizedCollectionActions = map[string]*structpb.ListValue{
|
||||
testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
|
||||
testAuthorizedVaultCollectionActions = map[string]*structpb.ListValue{
|
||||
"credential-libraries": {
|
||||
Values: []*structpb.Value{
|
||||
structpb.NewStringValue("create"),
|
||||
@@ -45,6 +46,14 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
testAuthorizedStaticCollectionActions = map[string]*structpb.ListValue{
|
||||
"credentials": {
|
||||
Values: []*structpb.Value{
|
||||
structpb.NewStringValue("create"),
|
||||
structpb.NewStringValue("list"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
@@ -60,9 +69,12 @@ func TestList(t *testing.T) {
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
repoFn := func() (*vault.Repository, error) {
|
||||
vaultRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
staticRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(context.Background(), rw, rw, kms)
|
||||
}
|
||||
|
||||
_, prjNoStores := iam.TestScopes(t, iamRepo)
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
@@ -78,7 +90,7 @@ func TestList(t *testing.T) {
|
||||
Version: s.GetVersion(),
|
||||
Type: vault.Subtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
AuthorizedCollectionActions: testAuthorizedCollectionActions,
|
||||
AuthorizedCollectionActions: testAuthorizedVaultCollectionActions,
|
||||
Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{
|
||||
VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{
|
||||
Address: wrapperspb.String(s.GetVaultAddress()),
|
||||
@@ -91,6 +103,20 @@ func TestList(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
for _, s := range credstatic.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 10) {
|
||||
wantStores = append(wantStores, &pb.CredentialStore{
|
||||
Id: s.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
CreatedTime: s.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: s.GetUpdateTime().GetTimestamp(),
|
||||
Version: s.GetVersion(),
|
||||
Type: credstatic.Subtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
|
||||
})
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.ListCredentialStoresRequest
|
||||
@@ -111,11 +137,17 @@ func TestList(t *testing.T) {
|
||||
anonRes: &pbs.ListCredentialStoresResponse{},
|
||||
},
|
||||
{
|
||||
name: "Filter to One Store",
|
||||
name: "Filter to One Vault Store",
|
||||
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantStores[1].GetId())},
|
||||
res: &pbs.ListCredentialStoresResponse{Items: wantStores[1:2]},
|
||||
anonRes: &pbs.ListCredentialStoresResponse{Items: wantStores[1:2]},
|
||||
},
|
||||
{
|
||||
name: "Filter to One static Store",
|
||||
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantStores[11].GetId())},
|
||||
res: &pbs.ListCredentialStoresResponse{Items: wantStores[11:12]},
|
||||
anonRes: &pbs.ListCredentialStoresResponse{Items: wantStores[11:12]},
|
||||
},
|
||||
{
|
||||
name: "Filter on Attribute",
|
||||
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/address"==%q`, wantStores[2].GetVaultCredentialStoreAttributes().GetAddress().Value)},
|
||||
@@ -136,7 +168,7 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s, err := NewService(repoFn, iamRepoFn)
|
||||
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
|
||||
require.NoError(t, err, "Couldn't create new host set service.")
|
||||
|
||||
// Test non-anonymous listing
|
||||
@@ -164,7 +196,7 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
func TestCreateVault(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
@@ -177,9 +209,12 @@ func TestCreate(t *testing.T) {
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
repoFn := func() (*vault.Repository, error) {
|
||||
vaultRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
staticRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(context.Background(), rw, rw, kms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
defaultCreated := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0].GetCreateTime().GetTimestamp()
|
||||
@@ -343,22 +378,6 @@ func TestCreate(t *testing.T) {
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Can't specify Update Time",
|
||||
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
|
||||
ScopeId: prj.GetPublicId(),
|
||||
UpdatedTime: timestamppb.Now(),
|
||||
Type: vault.Subtype.String(),
|
||||
Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{
|
||||
VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{
|
||||
Address: wrapperspb.String(v.Addr),
|
||||
Token: wrapperspb.String(newToken()),
|
||||
},
|
||||
},
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Must specify type",
|
||||
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
|
||||
@@ -433,7 +452,7 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
AuthorizedCollectionActions: testAuthorizedCollectionActions,
|
||||
AuthorizedCollectionActions: testAuthorizedVaultCollectionActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -474,7 +493,7 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
AuthorizedCollectionActions: testAuthorizedCollectionActions,
|
||||
AuthorizedCollectionActions: testAuthorizedVaultCollectionActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -483,7 +502,7 @@ func TestCreate(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
s, err := NewService(repoFn, iamRepoFn)
|
||||
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
|
||||
require.NoError(err, "Error when getting new credential store service.")
|
||||
defer cleanup(s)
|
||||
|
||||
@@ -533,6 +552,171 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateStatic(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
vaultRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
staticRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(context.Background(), rw, rw, kms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
defaultCreated := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
|
||||
cleanup := func(s Service) {
|
||||
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
|
||||
r, err := s.ListCredentialStores(ctx, &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId()})
|
||||
require.NoError(t, err)
|
||||
for _, i := range r.GetItems() {
|
||||
_, err := s.DeleteCredentialStore(ctx, &pbs.DeleteCredentialStoreRequest{Id: i.GetId()})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.CreateCredentialStoreRequest
|
||||
res *pbs.CreateCredentialStoreResponse
|
||||
idPrefix string
|
||||
err error
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Can't specify Id",
|
||||
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Id: credstatic.CredentialStorePrefix + "_notallowed",
|
||||
Type: credstatic.Subtype.String(),
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Can't specify Created Time",
|
||||
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
|
||||
ScopeId: prj.GetPublicId(),
|
||||
CreatedTime: timestamppb.Now(),
|
||||
Type: credstatic.Subtype.String(),
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Can't specify Update Time",
|
||||
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
|
||||
ScopeId: prj.GetPublicId(),
|
||||
UpdatedTime: timestamppb.Now(),
|
||||
Type: credstatic.Subtype.String(),
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Must specify type",
|
||||
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
|
||||
ScopeId: prj.GetPublicId(),
|
||||
}},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "Create a valid static CredentialStore",
|
||||
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Type: credstatic.Subtype.String(),
|
||||
}},
|
||||
idPrefix: credstatic.CredentialStorePrefix + "_",
|
||||
res: &pbs.CreateCredentialStoreResponse{
|
||||
Uri: fmt.Sprintf("credential-stores/%s_", credstatic.CredentialStorePrefix),
|
||||
Item: &pb.CredentialStore{
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: credstatic.Subtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create a valid static CredentialStore with name and description",
|
||||
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Name: &wrapperspb.StringValue{Value: "name"},
|
||||
Description: &wrapperspb.StringValue{Value: "desc"},
|
||||
Type: credstatic.Subtype.String(),
|
||||
}},
|
||||
idPrefix: credstatic.CredentialStorePrefix + "_",
|
||||
res: &pbs.CreateCredentialStoreResponse{
|
||||
Uri: fmt.Sprintf("credential-stores/%s_", credstatic.CredentialStorePrefix),
|
||||
Item: &pb.CredentialStore{
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
|
||||
Version: 1,
|
||||
Type: credstatic.Subtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Name: &wrapperspb.StringValue{Value: "name"},
|
||||
Description: &wrapperspb.StringValue{Value: "desc"},
|
||||
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
|
||||
require.NoError(err, "Error when getting new credential store service.")
|
||||
defer cleanup(s)
|
||||
|
||||
got, gErr := s.CreateCredentialStore(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetItem().GetScopeId()), tc.req)
|
||||
if tc.wantErr || tc.err != nil {
|
||||
require.Error(gErr)
|
||||
if tc.err != nil {
|
||||
assert.True(errors.Is(gErr, tc.err), "CreateCredentialStore(...) got error %v, wanted %v", gErr, tc.err)
|
||||
}
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
if tc.res == nil {
|
||||
require.Nil(got)
|
||||
}
|
||||
cmpOptions := []cmp.Option{
|
||||
protocmp.Transform(),
|
||||
protocmp.SortRepeatedFields(got),
|
||||
}
|
||||
if got != nil {
|
||||
assert.Contains(got.GetUri(), tc.res.Uri)
|
||||
cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pbs.CreateCredentialStoreResponse{}, "uri"))
|
||||
|
||||
assert.True(strings.HasPrefix(got.GetItem().GetId(), tc.idPrefix))
|
||||
cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pb.CredentialStore{}, "id"))
|
||||
|
||||
gotCreateTime := got.GetItem().GetCreatedTime()
|
||||
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
||||
|
||||
// Verify it is a credential store created after the test setup's default credential store
|
||||
assert.True(gotCreateTime.AsTime().After(defaultCreated.CreateTime.AsTime()), "New credential store should have been created after default credential store. Was created %v, which is after %v", gotCreateTime, defaultCreated)
|
||||
assert.True(gotUpdateTime.AsTime().After(defaultCreated.CreateTime.AsTime()), "New credential store should have been updated after default credential store. Was updated %v, which is after %v", gotUpdateTime, defaultCreated)
|
||||
cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pb.CredentialStore{}, "created_time"))
|
||||
cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pb.CredentialStore{}, "updated_time"))
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, cmpOptions...), "CreateCredentialStore(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
@@ -544,14 +728,18 @@ func TestGet(t *testing.T) {
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
repoFn := func() (*vault.Repository, error) {
|
||||
vaultRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
staticRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(context.Background(), rw, rw, kms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
||||
s, err := NewService(repoFn, iamRepoFn)
|
||||
vaultStore := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
|
||||
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
@@ -561,35 +749,57 @@ func TestGet(t *testing.T) {
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
id: store.GetPublicId(),
|
||||
name: "vault success",
|
||||
id: vaultStore.GetPublicId(),
|
||||
res: &pbs.GetCredentialStoreResponse{
|
||||
Item: &pb.CredentialStore{
|
||||
Id: store.GetPublicId(),
|
||||
ScopeId: store.GetScopeId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: store.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
Id: vaultStore.GetPublicId(),
|
||||
ScopeId: vaultStore.GetScopeId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: vaultStore.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
Type: vault.Subtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
AuthorizedCollectionActions: testAuthorizedCollectionActions,
|
||||
CreatedTime: store.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: store.UpdateTime.GetTimestamp(),
|
||||
AuthorizedCollectionActions: testAuthorizedVaultCollectionActions,
|
||||
CreatedTime: vaultStore.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: vaultStore.UpdateTime.GetTimestamp(),
|
||||
Version: 1,
|
||||
Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{
|
||||
VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{
|
||||
Address: wrapperspb.String(store.GetVaultAddress()),
|
||||
TokenHmac: base64.RawURLEncoding.EncodeToString(store.Token().GetTokenHmac()),
|
||||
ClientCertificate: wrapperspb.String(string(store.ClientCertificate().GetCertificate())),
|
||||
ClientCertificateKeyHmac: base64.RawURLEncoding.EncodeToString(store.ClientCertificate().GetCertificateKeyHmac()),
|
||||
Address: wrapperspb.String(vaultStore.GetVaultAddress()),
|
||||
TokenHmac: base64.RawURLEncoding.EncodeToString(vaultStore.Token().GetTokenHmac()),
|
||||
ClientCertificate: wrapperspb.String(string(vaultStore.ClientCertificate().GetCertificate())),
|
||||
ClientCertificateKeyHmac: base64.RawURLEncoding.EncodeToString(vaultStore.ClientCertificate().GetCertificateKeyHmac()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
name: "static success",
|
||||
id: staticStore.GetPublicId(),
|
||||
res: &pbs.GetCredentialStoreResponse{
|
||||
Item: &pb.CredentialStore{
|
||||
Id: staticStore.GetPublicId(),
|
||||
ScopeId: staticStore.GetScopeId(),
|
||||
Scope: &scopepb.ScopeInfo{Id: staticStore.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
|
||||
Type: credstatic.Subtype.String(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
|
||||
CreatedTime: staticStore.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: staticStore.UpdateTime.GetTimestamp(),
|
||||
Version: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vault not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", vault.CredentialStorePrefix),
|
||||
err: handlers.NotFoundError(),
|
||||
},
|
||||
{
|
||||
name: "static not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", credstatic.CredentialStorePrefix),
|
||||
err: handlers.NotFoundError(),
|
||||
},
|
||||
{
|
||||
name: "bad prefix",
|
||||
id: fmt.Sprintf("%s_1234567890", static.HostPrefix),
|
||||
@@ -632,14 +842,18 @@ func TestDelete(t *testing.T) {
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
repoFn := func() (*vault.Repository, error) {
|
||||
vaultRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
staticRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(context.Background(), rw, rw, kms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)[0]
|
||||
s, err := NewService(repoFn, iamRepoFn)
|
||||
vaultStore := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)[0]
|
||||
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
@@ -649,14 +863,23 @@ func TestDelete(t *testing.T) {
|
||||
res *pbs.DeleteCredentialStoreResponse
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
id: store.GetPublicId(),
|
||||
name: "vault success",
|
||||
id: vaultStore.GetPublicId(),
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
name: "static success",
|
||||
id: staticStore.GetPublicId(),
|
||||
},
|
||||
{
|
||||
name: "vault not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", vault.CredentialStorePrefix),
|
||||
err: handlers.NotFoundError(),
|
||||
},
|
||||
{
|
||||
name: "static not found error",
|
||||
id: fmt.Sprintf("%s_1234567890", credstatic.CredentialStorePrefix),
|
||||
err: handlers.NotFoundError(),
|
||||
},
|
||||
{
|
||||
name: "bad prefix",
|
||||
id: fmt.Sprintf("%s_1234567890", static.HostPrefix),
|
||||
@@ -680,7 +903,7 @@ func TestDelete(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
func TestUpdateVault(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
@@ -693,14 +916,17 @@ func TestUpdate(t *testing.T) {
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
repoFn := func() (*vault.Repository, error) {
|
||||
vaultRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
staticRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(context.Background(), rw, rw, kms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
|
||||
|
||||
s, err := NewService(repoFn, iamRepoFn)
|
||||
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
|
||||
@@ -1010,3 +1236,178 @@ func TestUpdate(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateStatic(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
vaultRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
staticRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(context.Background(), rw, rw, kms)
|
||||
}
|
||||
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
|
||||
|
||||
s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
|
||||
return &fieldmaskpb.FieldMask{Paths: paths}
|
||||
}
|
||||
|
||||
freshStore := func() (*credstatic.CredentialStore, func()) {
|
||||
t.Helper()
|
||||
st := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
clean := func() {
|
||||
_, err := s.DeleteCredentialStore(ctx, &pbs.DeleteCredentialStoreRequest{Id: st.GetPublicId()})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return st, clean
|
||||
}
|
||||
|
||||
successCases := []struct {
|
||||
name string
|
||||
req *pbs.UpdateCredentialStoreRequest
|
||||
res func(*pb.CredentialStore) *pb.CredentialStore
|
||||
}{
|
||||
{
|
||||
name: "name",
|
||||
req: &pbs.UpdateCredentialStoreRequest{
|
||||
UpdateMask: fieldmask("name"),
|
||||
Item: &pb.CredentialStore{
|
||||
Name: wrapperspb.String("new-name"),
|
||||
},
|
||||
},
|
||||
res: func(in *pb.CredentialStore) *pb.CredentialStore {
|
||||
out := proto.Clone(in).(*pb.CredentialStore)
|
||||
out.Name = wrapperspb.String("new-name")
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
req: &pbs.UpdateCredentialStoreRequest{
|
||||
UpdateMask: fieldmask("description"),
|
||||
Item: &pb.CredentialStore{
|
||||
Description: wrapperspb.String("new-description"),
|
||||
},
|
||||
},
|
||||
res: func(in *pb.CredentialStore) *pb.CredentialStore {
|
||||
out := proto.Clone(in).(*pb.CredentialStore)
|
||||
out.Description = wrapperspb.String("new-description")
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "name-and-description",
|
||||
req: &pbs.UpdateCredentialStoreRequest{
|
||||
UpdateMask: fieldmask("name", "description"),
|
||||
Item: &pb.CredentialStore{
|
||||
Name: wrapperspb.String("new-name"),
|
||||
Description: wrapperspb.String("new-description"),
|
||||
},
|
||||
},
|
||||
res: func(in *pb.CredentialStore) *pb.CredentialStore {
|
||||
out := proto.Clone(in).(*pb.CredentialStore)
|
||||
out.Name = wrapperspb.String("new-name")
|
||||
out.Description = wrapperspb.String("new-description")
|
||||
return out
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range successCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
st, cleanup := freshStore()
|
||||
defer cleanup()
|
||||
|
||||
if tc.req.Item.GetVersion() == 0 {
|
||||
tc.req.Item.Version = 1
|
||||
}
|
||||
if tc.req.GetId() == "" {
|
||||
tc.req.Id = st.GetPublicId()
|
||||
}
|
||||
resToChange, err := s.GetCredentialStore(ctx, &pbs.GetCredentialStoreRequest{Id: st.GetPublicId()})
|
||||
require.NoError(err)
|
||||
want := &pbs.UpdateCredentialStoreResponse{Item: tc.res(resToChange.GetItem())}
|
||||
|
||||
got, gErr := s.UpdateCredentialStore(ctx, tc.req)
|
||||
require.NoError(gErr)
|
||||
require.NotNil(got)
|
||||
|
||||
gotUpdateTime := got.GetItem().GetUpdatedTime()
|
||||
created := st.GetCreateTime().GetTimestamp()
|
||||
assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
|
||||
|
||||
want.Item.UpdatedTime = got.Item.UpdatedTime
|
||||
|
||||
assert.EqualValues(2, got.Item.Version)
|
||||
want.Item.Version = 2
|
||||
|
||||
assert.Empty(cmp.Diff(got, want, protocmp.Transform()))
|
||||
})
|
||||
}
|
||||
|
||||
// cant update read only fields
|
||||
st, cleanup := freshStore()
|
||||
defer cleanup()
|
||||
|
||||
roCases := []struct {
|
||||
path string
|
||||
item *pb.CredentialStore
|
||||
matcher func(t *testing.T, e error) // When not set defaults to checking against InvalidArgument Error
|
||||
}{
|
||||
{
|
||||
path: "type",
|
||||
item: &pb.CredentialStore{Type: "something"},
|
||||
},
|
||||
{
|
||||
path: "scope_id",
|
||||
item: &pb.CredentialStore{ScopeId: "global"},
|
||||
},
|
||||
{
|
||||
path: "updated_time",
|
||||
item: &pb.CredentialStore{UpdatedTime: timestamppb.Now()},
|
||||
},
|
||||
{
|
||||
path: "created_time",
|
||||
item: &pb.CredentialStore{UpdatedTime: timestamppb.Now()},
|
||||
},
|
||||
{
|
||||
path: "authorized_actions",
|
||||
item: &pb.CredentialStore{AuthorizedActions: append(testAuthorizedActions, "another")},
|
||||
},
|
||||
}
|
||||
for _, tc := range roCases {
|
||||
t.Run(fmt.Sprintf("ReadOnlyField/%s", tc.path), func(t *testing.T) {
|
||||
req := &pbs.UpdateCredentialStoreRequest{
|
||||
Id: st.GetPublicId(),
|
||||
Item: tc.item,
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{tc.path}},
|
||||
}
|
||||
req.Item.Version = st.Version
|
||||
|
||||
got, gErr := s.UpdateCredentialStore(ctx, req)
|
||||
assert.Error(t, gErr)
|
||||
matcher := tc.matcher
|
||||
if matcher == nil {
|
||||
matcher = func(t *testing.T, e error) {
|
||||
assert.Truef(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)), "got error %v, wanted invalid argument", gErr)
|
||||
}
|
||||
}
|
||||
matcher(t, gErr)
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package targets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
credstatic "github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
serverpb "github.com/hashicorp/boundary/internal/gen/controller/servers/services"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
// dynamicToWorkerCredential converts the strongly typed credential.Dynamic into
|
||||
// a session.Credential suitable for passing to a Boundary worker.
|
||||
func dynamicToWorkerCredential(ctx context.Context, cred credential.Dynamic) (session.Credential, error) {
|
||||
const op = "targets.dynamicToWorkerCredential"
|
||||
var workerCred *serverpb.Credential
|
||||
switch c := cred.(type) {
|
||||
case credential.UsernamePassword:
|
||||
workerCred = &serverpb.Credential{
|
||||
Credential: &serverpb.Credential_UserPassword{
|
||||
UserPassword: &serverpb.UserPassword{
|
||||
Username: c.Username(),
|
||||
Password: string(c.Password()),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(workerCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling dynamic secret to proto"))
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func dynamicToSessionCredential(ctx context.Context, cred credential.Dynamic) (*pb.SessionCredential, error) {
|
||||
const op = "targets.dynamicToSessionCredential"
|
||||
l := cred.Library()
|
||||
secret := cred.Secret()
|
||||
// TODO: Access the json directly from the vault response instead of re-marshalling it.
|
||||
jSecret, err := json.Marshal(secret)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling secret to json"))
|
||||
}
|
||||
var sSecret *structpb.Struct
|
||||
switch secret.(type) {
|
||||
case map[string]interface{}:
|
||||
// In this case we actually have to re-decode it. The proto wrappers
|
||||
// choke on json.Number and at the time I'm writing this I don't
|
||||
// have time to write a walk function to dig through with reflect
|
||||
// and find all json.Numbers and replace them. So we eat the
|
||||
// inefficiency. So note that we are specifically _not_ using a
|
||||
// decoder with UseNumber here.
|
||||
var dSecret map[string]interface{}
|
||||
if err := json.Unmarshal(jSecret, &dSecret); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("decoding json for proto marshaling"))
|
||||
}
|
||||
sSecret, err = structpb.NewStruct(dSecret)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for secret"))
|
||||
}
|
||||
}
|
||||
|
||||
var credType string
|
||||
var credData *structpb.Struct
|
||||
if l.CredentialType() != credential.UnspecifiedType {
|
||||
credType = string(l.CredentialType())
|
||||
|
||||
switch c := cred.(type) {
|
||||
case credential.UsernamePassword:
|
||||
credData, err = handlers.ProtoToStruct(
|
||||
&pb.UserPasswordCredential{
|
||||
Username: c.Username(),
|
||||
Password: string(c.Password()),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.SessionCredential{
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: l.GetPublicId(),
|
||||
Name: l.GetName(),
|
||||
Description: l.GetDescription(),
|
||||
CredentialStoreId: l.GetStoreId(),
|
||||
Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(),
|
||||
CredentialType: credType,
|
||||
},
|
||||
Secret: &pb.SessionSecret{
|
||||
Raw: base64.StdEncoding.EncodeToString(jSecret),
|
||||
Decoded: sSecret,
|
||||
},
|
||||
Credential: credData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// staticToWorkerCredential converts the credential.Static into
|
||||
// a session.Credential suitable for passing to a Boundary worker.
|
||||
func staticToWorkerCredential(ctx context.Context, cred credential.Static) (session.Credential, error) {
|
||||
const op = "targets.staticToWorkerCredential"
|
||||
var workerCred *serverpb.Credential
|
||||
switch c := cred.(type) {
|
||||
case credstatic.UsernamePasswordCredential:
|
||||
workerCred = &serverpb.Credential{
|
||||
Credential: &serverpb.Credential_UserPassword{
|
||||
UserPassword: &serverpb.UserPassword{
|
||||
Username: c.GetUsername(),
|
||||
Password: string(c.GetPassword()),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(workerCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling dynamic secret to proto"))
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func staticToSessionCredential(ctx context.Context, cred credential.Static) (*pb.SessionCredential, error) {
|
||||
const op = "targets.staticToSessionCredential"
|
||||
|
||||
var credType string
|
||||
var credData *structpb.Struct
|
||||
var secret map[string]interface{}
|
||||
switch c := cred.(type) {
|
||||
case *credstatic.UsernamePasswordCredential:
|
||||
var err error
|
||||
credType = string(credential.UsernamePasswordType)
|
||||
credData, err = handlers.ProtoToStruct(
|
||||
&pb.UserPasswordCredential{
|
||||
Username: c.GetUsername(),
|
||||
Password: string(c.GetPassword()),
|
||||
},
|
||||
)
|
||||
secret = map[string]interface{}{
|
||||
"username": c.GetUsername(),
|
||||
"password": string(c.GetPassword()),
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for static credential"))
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
|
||||
}
|
||||
|
||||
jSecret, err := json.Marshal(secret)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling static secret to json"))
|
||||
}
|
||||
sSecret, err := structpb.NewStruct(secret)
|
||||
|
||||
return &pb.SessionCredential{
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: cred.GetPublicId(),
|
||||
Name: cred.GetName(),
|
||||
Description: cred.GetDescription(),
|
||||
CredentialStoreId: cred.GetStoreId(),
|
||||
Type: credstatic.Subtype.String(),
|
||||
CredentialType: credType,
|
||||
},
|
||||
Secret: &pb.SessionSecret{
|
||||
Raw: base64.StdEncoding.EncodeToString(jSecret),
|
||||
Decoded: sSecret,
|
||||
},
|
||||
Credential: credData,
|
||||
}, nil
|
||||
}
|
||||
@@ -17,9 +17,15 @@ type Attributes interface {
|
||||
|
||||
// Options create target.Options to be used to create a target.Target.
|
||||
Options() []target.Option
|
||||
|
||||
// Vet validates the Attributes and returns a map of fields to error messages
|
||||
// if any fields are invalid.
|
||||
Vet() map[string]string
|
||||
|
||||
// VetForUpdate validates the Attributes for an updated resource with the
|
||||
// provided mask paths and returns a map of fields to error messages if any
|
||||
// fields are invalid.
|
||||
VetForUpdate([]string) map[string]string
|
||||
}
|
||||
|
||||
type attributeFunc func(interface{}) Attributes
|
||||
|
||||
@@ -2,16 +2,17 @@ package targets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
credstatic "github.com/hashicorp/boundary/internal/credential/static"
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/common"
|
||||
@@ -20,7 +21,6 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
serverpb "github.com/hashicorp/boundary/internal/gen/controller/servers/services"
|
||||
"github.com/hashicorp/boundary/internal/host"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
@@ -41,14 +41,14 @@ import (
|
||||
"github.com/mr-tron/base58"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
const (
|
||||
credentialDomain = "credential"
|
||||
hostDomain = "host"
|
||||
credentialDomain = "credential"
|
||||
hostDomain = "host"
|
||||
missingPortErrStr = "missing port in address"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -65,9 +65,6 @@ var (
|
||||
action.AddHostSources,
|
||||
action.SetHostSources,
|
||||
action.RemoveHostSources,
|
||||
action.AddCredentialLibraries,
|
||||
action.SetCredentialLibraries,
|
||||
action.RemoveCredentialLibraries,
|
||||
action.AddCredentialSources,
|
||||
action.SetCredentialSources,
|
||||
action.RemoveCredentialSources,
|
||||
@@ -93,6 +90,7 @@ type Service struct {
|
||||
pluginHostRepoFn common.PluginHostRepoFactory
|
||||
staticHostRepoFn common.StaticRepoFactory
|
||||
vaultCredRepoFn common.VaultCredentialRepoFactory
|
||||
staticCredRepoFn common.StaticCredentialRepoFactory
|
||||
kmsCache *kms.Kms
|
||||
}
|
||||
|
||||
@@ -107,6 +105,7 @@ func NewService(
|
||||
pluginHostRepoFn common.PluginHostRepoFactory,
|
||||
staticHostRepoFn common.StaticRepoFactory,
|
||||
vaultCredRepoFn common.VaultCredentialRepoFactory,
|
||||
staticCredRepoFn common.StaticCredentialRepoFactory,
|
||||
) (Service, error) {
|
||||
const op = "targets.NewService"
|
||||
if repoFn == nil {
|
||||
@@ -130,6 +129,9 @@ func NewService(
|
||||
if vaultCredRepoFn == nil {
|
||||
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing vault credential repository")
|
||||
}
|
||||
if staticCredRepoFn == nil {
|
||||
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static credential repository")
|
||||
}
|
||||
return Service{
|
||||
repoFn: repoFn,
|
||||
iamRepoFn: iamRepoFn,
|
||||
@@ -138,6 +140,7 @@ func NewService(
|
||||
pluginHostRepoFn: pluginHostRepoFn,
|
||||
staticHostRepoFn: staticHostRepoFn,
|
||||
vaultCredRepoFn: vaultCredRepoFn,
|
||||
staticCredRepoFn: staticCredRepoFn,
|
||||
kmsCache: kmsCache,
|
||||
}, nil
|
||||
}
|
||||
@@ -486,7 +489,7 @@ func (s Service) AddTargetHostSources(ctx context.Context, req *pbs.AddTargetHos
|
||||
if err := validateAddHostSourcesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddHostSets)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddHostSources)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -524,7 +527,7 @@ func (s Service) SetTargetHostSources(ctx context.Context, req *pbs.SetTargetHos
|
||||
if err := validateSetHostSourcesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetHostSets)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetHostSources)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -557,12 +560,12 @@ func (s Service) SetTargetHostSources(ctx context.Context, req *pbs.SetTargetHos
|
||||
|
||||
// RemoveTargetHostSources implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) RemoveTargetHostSources(ctx context.Context, req *pbs.RemoveTargetHostSourcesRequest) (*pbs.RemoveTargetHostSourcesResponse, error) {
|
||||
const op = "targets.(Service).RemoveTargetHostSets"
|
||||
const op = "targets.(Service).RemoveTargetHostSources"
|
||||
|
||||
if err := validateRemoveHostSourcesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveHostSets)
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveHostSources)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
@@ -593,120 +596,6 @@ func (s Service) RemoveTargetHostSources(ctx context.Context, req *pbs.RemoveTar
|
||||
return &pbs.RemoveTargetHostSourcesResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DEPRECATED: AddTargetCredentialLibraries implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) AddTargetCredentialLibraries(ctx context.Context, req *pbs.AddTargetCredentialLibrariesRequest) (*pbs.AddTargetCredentialLibrariesResponse, error) {
|
||||
const op = "targets.(Service).AddTargetCredentialLibraries"
|
||||
|
||||
if err := validateAddLibrariesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddCredentialLibraries)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
t, ts, cl, err := s.addCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, t, ts, cl, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.AddTargetCredentialLibrariesResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DEPRECATED: SetTargetCredentialLibraries implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) SetTargetCredentialLibraries(ctx context.Context, req *pbs.SetTargetCredentialLibrariesRequest) (*pbs.SetTargetCredentialLibrariesResponse, error) {
|
||||
const op = "targets.(Service).SetTargetCredentialLibraries"
|
||||
|
||||
if err := validateSetLibrariesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetCredentialLibraries)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
t, ts, cl, err := s.setCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, t, ts, cl, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.SetTargetCredentialLibrariesResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DEPRECATED: RemoveTargetCredentialLibraries implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) RemoveTargetCredentialLibraries(ctx context.Context, req *pbs.RemoveTargetCredentialLibrariesRequest) (*pbs.RemoveTargetCredentialLibrariesResponse, error) {
|
||||
const op = "targets.(Service).RemoveTargetCredentialLibraries"
|
||||
|
||||
if err := validateRemoveLibrariesRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveCredentialLibraries)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
t, ts, cl, err := s.removeCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, t, ts, cl, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.RemoveTargetCredentialLibrariesResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// AddTargetCredentialSources implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) AddTargetCredentialSources(ctx context.Context, req *pbs.AddTargetCredentialSourcesRequest) (*pbs.AddTargetCredentialSourcesResponse, error) {
|
||||
const op = "targets.(Service).AddTargetCredentialSources"
|
||||
@@ -716,7 +605,12 @@ func (s Service) AddTargetCredentialSources(ctx context.Context, req *pbs.AddTar
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.AddCredentialSources)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
// TODO AddCredentialLibraries was deprecated but grant actions were never migrated
|
||||
// remove this check once actions have been migrated
|
||||
authResults = s.authResult(ctx, req.GetId(), action.AddCredentialLibraries)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
}
|
||||
t, ts, cl, err := s.addCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
@@ -754,7 +648,12 @@ func (s Service) SetTargetCredentialSources(ctx context.Context, req *pbs.SetTar
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.SetCredentialSources)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
// TODO SetCredentialLibraries was deprecated but grant actions were never migrated
|
||||
// remove this check once actions have been migrated
|
||||
authResults = s.authResult(ctx, req.GetId(), action.SetCredentialLibraries)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
}
|
||||
t, ts, cl, err := s.setCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
@@ -792,7 +691,12 @@ func (s Service) RemoveTargetCredentialSources(ctx context.Context, req *pbs.Rem
|
||||
}
|
||||
authResults := s.authResult(ctx, req.GetId(), action.RemoveCredentialSources)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
// TODO RemoveCredentialLibraries was deprecated but grant actions were never migrated
|
||||
// remove this check once actions have been migrated
|
||||
authResults = s.authResult(ctx, req.GetId(), action.RemoveCredentialLibraries)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
}
|
||||
t, ts, cl, err := s.removeCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
@@ -869,7 +773,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t, hostSources, libs, err := repo.LookupTarget(ctx, t.GetPublicId())
|
||||
t, hostSources, credSources, err := repo.LookupTarget(ctx, t.GetPublicId())
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, handlers.NotFoundErrorf("Target %q not found.", t.GetPublicId())
|
||||
@@ -978,25 +882,39 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
|
||||
chosenEndpoint = endpoints[rand.Intn(len(endpoints))]
|
||||
}
|
||||
|
||||
h, p, err := net.SplitHostPort(chosenEndpoint.Address)
|
||||
switch {
|
||||
case err != nil && strings.Contains(err.Error(), missingPortErrStr):
|
||||
if t.GetDefaultPort() == 0 {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("neither the selected host %q nor the target provides a port to use", chosenEndpoint.HostId))
|
||||
}
|
||||
h = chosenEndpoint.Address
|
||||
p = strconv.FormatUint(uint64(t.GetDefaultPort()), 10)
|
||||
case err != nil:
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error when parsing the chosen endpoints host address"))
|
||||
}
|
||||
// Generate the endpoint URL
|
||||
endpointUrl := &url.URL{
|
||||
Scheme: t.GetType().String(),
|
||||
}
|
||||
defaultPort := t.GetDefaultPort()
|
||||
if defaultPort != 0 {
|
||||
endpointUrl.Host = fmt.Sprintf("%s:%d", chosenEndpoint.Address, defaultPort)
|
||||
} else {
|
||||
endpointUrl.Host = chosenEndpoint.Address
|
||||
Host: net.JoinHostPort(h, p),
|
||||
}
|
||||
|
||||
var reqs []credential.Request
|
||||
var vaultReqs []credential.Request
|
||||
var staticIds []string
|
||||
var dynCreds []*session.DynamicCredential
|
||||
for _, l := range libs {
|
||||
reqs = append(reqs, credential.Request{
|
||||
SourceId: l.Id(),
|
||||
Purpose: l.CredentialPurpose(),
|
||||
})
|
||||
dynCreds = append(dynCreds, session.NewDynamicCredential(l.Id(), l.CredentialPurpose()))
|
||||
var staticCreds []*session.StaticCredential
|
||||
for _, cs := range credSources {
|
||||
switch cs.Type() {
|
||||
case target.LibraryCredentialSourceType:
|
||||
vaultReqs = append(vaultReqs, credential.Request{
|
||||
SourceId: cs.Id(),
|
||||
Purpose: cs.CredentialPurpose(),
|
||||
})
|
||||
dynCreds = append(dynCreds, session.NewDynamicCredential(cs.Id(), cs.CredentialPurpose()))
|
||||
case target.StaticCredentialSourceType:
|
||||
staticIds = append(staticIds, cs.Id())
|
||||
staticCreds = append(staticCreds, session.NewStaticCredential(cs.Id(), cs.CredentialPurpose()))
|
||||
}
|
||||
}
|
||||
|
||||
expTime := timestamppb.Now()
|
||||
@@ -1013,6 +931,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
|
||||
ConnectionLimit: t.GetSessionConnectionLimit(),
|
||||
WorkerFilter: t.GetWorkerFilter(),
|
||||
DynamicCredentials: dynCreds,
|
||||
StaticCredentials: staticCreds,
|
||||
}
|
||||
|
||||
sess, err := session.New(sessionComposition)
|
||||
@@ -1028,111 +947,82 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cs []credential.Dynamic
|
||||
if len(reqs) > 0 {
|
||||
var dynamic []credential.Dynamic
|
||||
var staticCredsById map[string]credential.Static
|
||||
if len(vaultReqs) > 0 {
|
||||
credRepo, err := s.vaultCredRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
cs, err = credRepo.Issue(ctx, sess.GetPublicId(), reqs)
|
||||
dynamic, err = credRepo.Issue(ctx, sess.GetPublicId(), vaultReqs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
}
|
||||
|
||||
if len(staticIds) > 0 {
|
||||
credRepo, err := s.staticCredRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
// Remove duplicate requests
|
||||
staticIds = strutil.RemoveDuplicates(staticIds, false)
|
||||
creds, err := credRepo.Retrieve(ctx, t.GetScopeId(), staticIds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
staticCredsById = make(map[string]credential.Static)
|
||||
for _, c := range creds {
|
||||
staticCredsById[c.GetPublicId()] = c
|
||||
}
|
||||
}
|
||||
|
||||
var creds []*pb.SessionCredential
|
||||
var workerCreds []session.Credential
|
||||
for _, cred := range cs {
|
||||
for _, cred := range dynamic {
|
||||
switch cred.Purpose() {
|
||||
case credential.EgressPurpose:
|
||||
m, err := credentialToProto(ctx, cred)
|
||||
c, err := dynamicToWorkerCredential(ctx, cred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
data, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling secret to proto"))
|
||||
}
|
||||
workerCreds = append(workerCreds, data)
|
||||
workerCreds = append(workerCreds, c)
|
||||
|
||||
case credential.ApplicationPurpose:
|
||||
l := cred.Library()
|
||||
secret := cred.Secret()
|
||||
// TODO: Access the json directly from the vault response instead of re-marshalling it.
|
||||
jSecret, err := json.Marshal(secret)
|
||||
c, err := dynamicToSessionCredential(ctx, cred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling secret to json"))
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
var sSecret *structpb.Struct
|
||||
switch secret.(type) {
|
||||
case map[string]interface{}:
|
||||
// In this case we actually have to re-decode it. The proto wrappers
|
||||
// choke on json.Number and at the time I'm writing this I don't
|
||||
// have time to write a walk function to dig through with reflect
|
||||
// and find all json.Numbers and replace them. So we eat the
|
||||
// inefficiency. So note that we are specifically _not_ using a
|
||||
// decoder with UseNumber here.
|
||||
var dSecret map[string]interface{}
|
||||
if err := json.Unmarshal(jSecret, &dSecret); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("decoding json for proto marshaling"))
|
||||
}
|
||||
sSecret, err = structpb.NewStruct(dSecret)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for secret"))
|
||||
}
|
||||
}
|
||||
|
||||
var credType string
|
||||
var credData *structpb.Struct
|
||||
if l.CredentialType() != credential.UnspecifiedType {
|
||||
credType = string(l.CredentialType())
|
||||
|
||||
switch c := cred.(type) {
|
||||
case credential.UserPassword:
|
||||
credData, err = handlers.ProtoToStruct(
|
||||
&pb.UserPasswordCredential{
|
||||
Username: c.Username(),
|
||||
Password: string(c.Password()),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unsupported credential type"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
creds = append(creds, &pb.SessionCredential{
|
||||
CredentialLibrary: &pb.CredentialLibrary{
|
||||
Id: l.GetPublicId(),
|
||||
Name: l.GetName(),
|
||||
Description: l.GetDescription(),
|
||||
CredentialStoreId: l.GetStoreId(),
|
||||
Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(),
|
||||
},
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: l.GetPublicId(),
|
||||
Name: l.GetName(),
|
||||
Description: l.GetDescription(),
|
||||
CredentialStoreId: l.GetStoreId(),
|
||||
Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(),
|
||||
CredentialType: credType,
|
||||
},
|
||||
Secret: &pb.SessionSecret{
|
||||
Raw: base64.StdEncoding.EncodeToString(jSecret),
|
||||
Decoded: sSecret,
|
||||
},
|
||||
Credential: credData,
|
||||
})
|
||||
creds = append(creds, c)
|
||||
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.Unknown, op, fmt.Sprintf("unsupported credential purpose %s", cred.Purpose()))
|
||||
}
|
||||
}
|
||||
|
||||
for _, sc := range staticCreds {
|
||||
switch sc.CredentialPurpose {
|
||||
case string(credential.EgressPurpose):
|
||||
c, err := staticToWorkerCredential(ctx, staticCredsById[sc.CredentialStaticId])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
workerCreds = append(workerCreds, c)
|
||||
|
||||
case string(credential.ApplicationPurpose):
|
||||
c, err := staticToSessionCredential(ctx, staticCredsById[sc.CredentialStaticId])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
creds = append(creds, c)
|
||||
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.Unknown, op, fmt.Sprintf("unsupported credential purpose %s", sc.CredentialPurpose))
|
||||
}
|
||||
}
|
||||
|
||||
if len(workerCreds) > 0 {
|
||||
// store credentials in repo, worker will request creds when a connection is established
|
||||
err = sessionRepo.AddSessionCredentials(ctx, sess.ScopeId, sess.PublicId, workerCreds)
|
||||
@@ -1166,7 +1056,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
|
||||
Scope: authResults.Scope,
|
||||
CreatedTime: sess.CreateTime.GetTimestamp(),
|
||||
Type: t.GetType().String(),
|
||||
AuthorizationToken: string(encodedMarshaledSad),
|
||||
AuthorizationToken: encodedMarshaledSad,
|
||||
UserId: authResults.UserId,
|
||||
HostId: chosenEndpoint.HostId,
|
||||
HostSetId: chosenEndpoint.SetId,
|
||||
@@ -1267,7 +1157,9 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st
|
||||
if err != nil {
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build target for update: %v.", err)
|
||||
}
|
||||
u.SetPublicId(ctx, id)
|
||||
if err := u.SetPublicId(ctx, id); err != nil {
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set target id: %v.", err)
|
||||
}
|
||||
|
||||
maskManager, err := subtypeRegistry.maskManager(subtype)
|
||||
if err != nil {
|
||||
@@ -1384,12 +1276,16 @@ func (s Service) addCredentialSourcesInRepo(ctx context.Context, targetId string
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
credLibs, err := createCredLibs(targetId, applicationIds, nil, egressIds)
|
||||
if err != nil {
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err)
|
||||
|
||||
var creds target.CredentialSources
|
||||
if len(applicationIds) > 0 {
|
||||
creds.ApplicationCredentialIds = strutil.RemoveDuplicates(applicationIds, false)
|
||||
}
|
||||
if len(egressIds) > 0 {
|
||||
creds.EgressCredentialIds = strutil.RemoveDuplicates(egressIds, false)
|
||||
}
|
||||
|
||||
out, hs, credSources, err := repo.AddTargetCredentialSources(ctx, targetId, version, credLibs)
|
||||
out, hs, credSources, err := repo.AddTargetCredentialSources(ctx, targetId, version, creds)
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add credential sources to target: %v.", err)
|
||||
@@ -1407,12 +1303,15 @@ func (s Service) setCredentialSourcesInRepo(ctx context.Context, targetId string
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
credLibs, err := createCredLibs(targetId, applicationIds, nil, egressIds)
|
||||
if err != nil {
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err)
|
||||
var ids target.CredentialSources
|
||||
if len(applicationIds) > 0 {
|
||||
ids.ApplicationCredentialIds = strutil.RemoveDuplicates(applicationIds, false)
|
||||
}
|
||||
if len(egressIds) > 0 {
|
||||
ids.EgressCredentialIds = strutil.RemoveDuplicates(egressIds, false)
|
||||
}
|
||||
|
||||
_, _, _, err = repo.SetTargetCredentialSources(ctx, targetId, version, credLibs)
|
||||
_, _, _, err = repo.SetTargetCredentialSources(ctx, targetId, version, ids)
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err)
|
||||
@@ -1435,11 +1334,14 @@ func (s Service) removeCredentialSourcesInRepo(ctx context.Context, targetId str
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
credLibs, err := createCredLibs(targetId, applicationIds, nil, egressIds)
|
||||
if err != nil {
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err)
|
||||
var ids target.CredentialSources
|
||||
if len(applicationIds) > 0 {
|
||||
ids.ApplicationCredentialIds = strutil.RemoveDuplicates(applicationIds, false)
|
||||
}
|
||||
_, err = repo.DeleteTargetCredentialSources(ctx, targetId, version, credLibs)
|
||||
if len(egressIds) > 0 {
|
||||
ids.EgressCredentialIds = strutil.RemoveDuplicates(egressIds, false)
|
||||
}
|
||||
_, err = repo.DeleteTargetCredentialSources(ctx, targetId, version, ids)
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove credential sources from target: %v.", err)
|
||||
@@ -1584,7 +1486,6 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou
|
||||
|
||||
var appCredSources, egressCredSources []*pb.CredentialSource
|
||||
var appCredSourceIds, egressCredSourceIds []string
|
||||
var appCredLibraries []*pb.CredentialLibrary
|
||||
|
||||
for _, cs := range credSources {
|
||||
switch cs.CredentialPurpose() {
|
||||
@@ -1595,13 +1496,6 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou
|
||||
CredentialStoreId: cs.CredentialStoreId(),
|
||||
})
|
||||
|
||||
// ApplicationCredentialLibrariesField is deprecated and should only be populated
|
||||
// for application purpose
|
||||
appCredLibraries = append(appCredLibraries, &pb.CredentialLibrary{
|
||||
Id: cs.Id(),
|
||||
CredentialStoreId: cs.CredentialStoreId(),
|
||||
})
|
||||
|
||||
case credential.EgressPurpose:
|
||||
egressCredSources = append(egressCredSources, &pb.CredentialSource{
|
||||
Id: cs.Id(),
|
||||
@@ -1614,15 +1508,9 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou
|
||||
}
|
||||
}
|
||||
|
||||
if outputFields.Has(globals.ApplicationCredentialLibraryIdsField) {
|
||||
out.ApplicationCredentialLibraryIds = appCredSourceIds
|
||||
}
|
||||
if outputFields.Has(globals.ApplicationCredentialSourceIdsField) {
|
||||
out.ApplicationCredentialSourceIds = appCredSourceIds
|
||||
}
|
||||
if outputFields.Has(globals.ApplicationCredentialLibrariesField) {
|
||||
out.ApplicationCredentialLibraries = appCredLibraries
|
||||
}
|
||||
if outputFields.Has(globals.ApplicationCredentialSourcesField) {
|
||||
out.ApplicationCredentialSources = appCredSources
|
||||
}
|
||||
@@ -1702,7 +1590,8 @@ func validateCreateRequest(req *pbs.CreateTargetRequest) error {
|
||||
func validateUpdateRequest(req *pbs.UpdateTargetRequest) error {
|
||||
return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
|
||||
badFields := map[string]string{}
|
||||
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), globals.NameField) && req.GetItem().GetName().GetValue() == "" {
|
||||
paths := req.GetUpdateMask().GetPaths()
|
||||
if handlers.MaskContains(paths, globals.NameField) && req.GetItem().GetName().GetValue() == "" {
|
||||
badFields[globals.NameField] = "This field cannot be set to empty."
|
||||
}
|
||||
if req.GetItem().GetSessionConnectionLimit() != nil {
|
||||
@@ -1735,7 +1624,7 @@ func validateUpdateRequest(req *pbs.UpdateTargetRequest) error {
|
||||
if err != nil {
|
||||
badFields[globals.AttributesField] = "Attribute fields do not match the expected format."
|
||||
} else {
|
||||
for k, v := range a.Vet() {
|
||||
for k, v := range a.VetForUpdate(paths) {
|
||||
badFields[k] = v
|
||||
}
|
||||
}
|
||||
@@ -1901,72 +1790,6 @@ func validateRemoveHostSourcesRequest(req *pbs.RemoveTargetHostSourcesRequest) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAddLibrariesRequest(req *pbs.AddTargetCredentialLibrariesRequest) error {
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) {
|
||||
badFields[globals.IdField] = "Incorrectly formatted identifier."
|
||||
}
|
||||
if req.GetVersion() == 0 {
|
||||
badFields[globals.VersionField] = "Required field."
|
||||
}
|
||||
if len(req.GetApplicationCredentialLibraryIds()) == 0 {
|
||||
badFields[globals.ApplicationCredentialLibraryIdsField] = "Must be non-empty."
|
||||
}
|
||||
for _, cl := range req.GetApplicationCredentialLibraryIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
|
||||
badFields[globals.ApplicationCredentialLibraryIdsField] = fmt.Sprintf("Incorrectly formatted credential library identifier %q.", cl)
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(badFields) > 0 {
|
||||
return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateSetLibrariesRequest(req *pbs.SetTargetCredentialLibrariesRequest) error {
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) {
|
||||
badFields[globals.IdField] = "Incorrectly formatted identifier."
|
||||
}
|
||||
if req.GetVersion() == 0 {
|
||||
badFields[globals.VersionField] = "Required field."
|
||||
}
|
||||
for _, cl := range req.GetApplicationCredentialLibraryIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
|
||||
badFields[globals.ApplicationCredentialLibraryIdsField] = fmt.Sprintf("Incorrectly formatted credential library identifier %q.", cl)
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(badFields) > 0 {
|
||||
return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateRemoveLibrariesRequest(req *pbs.RemoveTargetCredentialLibrariesRequest) error {
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) {
|
||||
badFields[globals.IdField] = "Incorrectly formatted identifier."
|
||||
}
|
||||
if req.GetVersion() == 0 {
|
||||
badFields[globals.VersionField] = "Required field."
|
||||
}
|
||||
if len(req.GetApplicationCredentialLibraryIds()) == 0 {
|
||||
badFields[globals.ApplicationCredentialLibraryIdsField] = "Must be non-empty."
|
||||
}
|
||||
for _, cl := range req.GetApplicationCredentialLibraryIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
|
||||
badFields[globals.ApplicationCredentialLibraryIdsField] = fmt.Sprintf("Incorrectly formatted credential library identifier %q.", cl)
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(badFields) > 0 {
|
||||
return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequest) error {
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) {
|
||||
@@ -1980,13 +1803,13 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ
|
||||
badFields[globals.EgressCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided."
|
||||
}
|
||||
for _, cl := range req.GetApplicationCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
|
||||
badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, cl := range req.GetEgressCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
|
||||
badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
|
||||
break
|
||||
}
|
||||
@@ -2006,13 +1829,13 @@ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequ
|
||||
badFields[globals.VersionField] = "Required field."
|
||||
}
|
||||
for _, cl := range req.GetApplicationCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
|
||||
badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, cl := range req.GetEgressCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
|
||||
badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
|
||||
break
|
||||
}
|
||||
@@ -2036,13 +1859,13 @@ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourc
|
||||
badFields[globals.EgressCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided."
|
||||
}
|
||||
for _, cl := range req.GetApplicationCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
|
||||
badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, cl := range req.GetEgressCredentialSourceIds() {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) {
|
||||
if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) {
|
||||
badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl)
|
||||
break
|
||||
}
|
||||
@@ -2094,45 +1917,6 @@ func validateAuthorizeSessionRequest(req *pbs.AuthorizeSessionRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createCredLibs(targetId string, applicationIds, ingressIds, egressIds []string) ([]*target.CredentialLibrary, error) {
|
||||
credLibs := make([]*target.CredentialLibrary, 0, len(applicationIds)+len(ingressIds)+len(egressIds))
|
||||
|
||||
byPurpose := map[credential.Purpose][]string{
|
||||
credential.ApplicationPurpose: strutil.RemoveDuplicates(applicationIds, false),
|
||||
credential.IngressPurpose: strutil.RemoveDuplicates(ingressIds, false),
|
||||
credential.EgressPurpose: strutil.RemoveDuplicates(egressIds, false),
|
||||
}
|
||||
for purpose, ids := range byPurpose {
|
||||
for _, id := range ids {
|
||||
l, err := target.NewCredentialLibrary(targetId, id, purpose)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
credLibs = append(credLibs, l)
|
||||
}
|
||||
}
|
||||
return credLibs, nil
|
||||
}
|
||||
|
||||
// credentialToProto converts the strongly typed credential.Credential into a known proto.Message.
|
||||
func credentialToProto(ctx context.Context, cred credential.Credential) (*serverpb.Credential, error) {
|
||||
const op = "targets.credentialToProto"
|
||||
switch c := cred.(type) {
|
||||
case credential.UserPassword:
|
||||
return &serverpb.Credential{
|
||||
Credential: &serverpb.Credential_UserPassword{
|
||||
UserPassword: &serverpb.UserPassword{
|
||||
Username: c.Username(),
|
||||
Password: string(c.Password()),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
|
||||
}
|
||||
}
|
||||
|
||||
// workerList is a helper type to make the selection of workers clearer and more declarative.
|
||||
type workerList []*servers.Worker
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -10,6 +10,8 @@ import (
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
|
||||
)
|
||||
|
||||
const defaultPortField = "attributes.default_port"
|
||||
|
||||
type attribute struct {
|
||||
*pb.TcpTargetAttributes
|
||||
}
|
||||
@@ -24,8 +26,23 @@ func (a *attribute) Options() []target.Option {
|
||||
|
||||
func (a *attribute) Vet() map[string]string {
|
||||
badFields := map[string]string{}
|
||||
if a.GetDefaultPort() != nil && a.GetDefaultPort().GetValue() == 0 {
|
||||
badFields["attributes.default_port"] = "This optional field cannot be set to 0."
|
||||
if a.GetDefaultPort() == nil {
|
||||
badFields["attributes.default_port"] = "This field is required."
|
||||
} else if a.GetDefaultPort().GetValue() == 0 {
|
||||
badFields["attributes.default_port"] = "This field cannot be set to zero."
|
||||
}
|
||||
return badFields
|
||||
}
|
||||
|
||||
func (a *attribute) VetForUpdate(p []string) map[string]string {
|
||||
if !handlers.MaskContains(p, defaultPortField) {
|
||||
return nil
|
||||
}
|
||||
badFields := map[string]string{}
|
||||
if a.GetDefaultPort() == nil {
|
||||
badFields["attributes.default_port"] = "This field is required."
|
||||
} else if a.GetDefaultPort().GetValue() == 0 {
|
||||
badFields["attributes.default_port"] = "This cannot be set to zero."
|
||||
}
|
||||
return badFields
|
||||
}
|
||||
|
||||
@@ -75,17 +75,18 @@ func TestGet(t *testing.T) {
|
||||
copy(kmsAuthzActions, testAuthorizedActions)
|
||||
|
||||
wantKmsWorker := &pb.Worker{
|
||||
Id: kmsWorker.GetPublicId(),
|
||||
ScopeId: kmsWorker.GetScopeId(),
|
||||
Scope: &scopes.ScopeInfo{Id: kmsWorker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
|
||||
CreatedTime: kmsWorker.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: kmsWorker.UpdateTime.GetTimestamp(),
|
||||
Version: kmsWorker.GetVersion(),
|
||||
Name: wrapperspb.String(kmsWorker.GetName()),
|
||||
Description: wrapperspb.String(kmsWorker.GetDescription()),
|
||||
Address: kmsWorker.GetAddress(),
|
||||
AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()),
|
||||
LastStatusTime: kmsWorker.GetLastStatusTime().GetTimestamp(),
|
||||
Id: kmsWorker.GetPublicId(),
|
||||
ScopeId: kmsWorker.GetScopeId(),
|
||||
Scope: &scopes.ScopeInfo{Id: kmsWorker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
|
||||
CreatedTime: kmsWorker.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: kmsWorker.UpdateTime.GetTimestamp(),
|
||||
Version: kmsWorker.GetVersion(),
|
||||
Name: wrapperspb.String(kmsWorker.GetName()),
|
||||
Description: wrapperspb.String(kmsWorker.GetDescription()),
|
||||
Address: kmsWorker.GetAddress(),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()),
|
||||
LastStatusTime: kmsWorker.GetLastStatusTime().GetTimestamp(),
|
||||
CanonicalTags: map[string]*structpb.ListValue{
|
||||
"key": structListValue(t, "val"),
|
||||
},
|
||||
@@ -114,17 +115,18 @@ func TestGet(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
wantPkiWorker := &pb.Worker{
|
||||
Id: pkiWorker.GetPublicId(),
|
||||
ScopeId: pkiWorker.GetScopeId(),
|
||||
Scope: &scopes.ScopeInfo{Id: pkiWorker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
|
||||
CreatedTime: pkiWorker.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: pkiWorker.UpdateTime.GetTimestamp(),
|
||||
Version: pkiWorker.GetVersion(),
|
||||
Name: wrapperspb.String(pkiWorker.GetName()),
|
||||
Description: wrapperspb.String(pkiWorker.GetDescription()),
|
||||
Address: pkiWorker.GetAddress(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
LastStatusTime: pkiWorker.GetLastStatusTime().GetTimestamp(),
|
||||
Id: pkiWorker.GetPublicId(),
|
||||
ScopeId: pkiWorker.GetScopeId(),
|
||||
Scope: &scopes.ScopeInfo{Id: pkiWorker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
|
||||
CreatedTime: pkiWorker.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: pkiWorker.UpdateTime.GetTimestamp(),
|
||||
Version: pkiWorker.GetVersion(),
|
||||
Name: wrapperspb.String(pkiWorker.GetName()),
|
||||
Description: wrapperspb.String(pkiWorker.GetDescription()),
|
||||
Address: pkiWorker.GetAddress(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
LastStatusTime: pkiWorker.GetLastStatusTime().GetTimestamp(),
|
||||
CanonicalTags: map[string]*structpb.ListValue{
|
||||
"config": structListValue(t, "test"),
|
||||
},
|
||||
@@ -208,17 +210,18 @@ func TestList(t *testing.T) {
|
||||
kmsAuthzActions := make([]string, len(testAuthorizedActions))
|
||||
copy(kmsAuthzActions, testAuthorizedActions)
|
||||
wantKmsWorkers = append(wantKmsWorkers, &pb.Worker{
|
||||
Id: w.GetPublicId(),
|
||||
ScopeId: w.GetScopeId(),
|
||||
Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
|
||||
CreatedTime: w.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: w.UpdateTime.GetTimestamp(),
|
||||
Version: w.GetVersion(),
|
||||
Name: wrapperspb.String(w.GetName()),
|
||||
AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()),
|
||||
Address: w.GetAddress(),
|
||||
Type: KmsWorkerType,
|
||||
LastStatusTime: w.GetLastStatusTime().GetTimestamp(),
|
||||
Id: w.GetPublicId(),
|
||||
ScopeId: w.GetScopeId(),
|
||||
Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
|
||||
CreatedTime: w.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: w.UpdateTime.GetTimestamp(),
|
||||
Version: w.GetVersion(),
|
||||
Name: wrapperspb.String(w.GetName()),
|
||||
AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
Address: w.GetAddress(),
|
||||
Type: KmsWorkerType,
|
||||
LastStatusTime: w.GetLastStatusTime().GetTimestamp(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -226,17 +229,18 @@ func TestList(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
w := servers.TestPkiWorker(t, conn, wrap, servers.WithName(fmt.Sprintf("pki-worker%d", i)))
|
||||
wantPkiWorkers = append(wantPkiWorkers, &pb.Worker{
|
||||
Id: w.GetPublicId(),
|
||||
ScopeId: w.GetScopeId(),
|
||||
Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
|
||||
CreatedTime: w.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: w.UpdateTime.GetTimestamp(),
|
||||
Version: w.GetVersion(),
|
||||
Name: wrapperspb.String(w.GetName()),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Address: w.GetAddress(),
|
||||
Type: PkiWorkerType,
|
||||
LastStatusTime: w.GetLastStatusTime().GetTimestamp(),
|
||||
Id: w.GetPublicId(),
|
||||
ScopeId: w.GetScopeId(),
|
||||
Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
|
||||
CreatedTime: w.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: w.UpdateTime.GetTimestamp(),
|
||||
Version: w.GetVersion(),
|
||||
Name: wrapperspb.String(w.GetName()),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Address: w.GetAddress(),
|
||||
Type: PkiWorkerType,
|
||||
LastStatusTime: w.GetLastStatusTime().GetTimestamp(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -440,15 +444,16 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("name"),
|
||||
Description: wrapperspb.String("desc"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("name"),
|
||||
Description: wrapperspb.String("desc"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -465,15 +470,16 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("name"),
|
||||
Description: wrapperspb.String("desc"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("name"),
|
||||
Description: wrapperspb.String("desc"),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -533,14 +539,15 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Description: wrapperspb.String("default"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Description: wrapperspb.String("default"),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -556,14 +563,15 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("default"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("default"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -580,15 +588,16 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("updated"),
|
||||
Description: wrapperspb.String("default"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("updated"),
|
||||
Description: wrapperspb.String("default"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -605,15 +614,16 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("default"),
|
||||
Description: wrapperspb.String("notignored"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("default"),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
Description: wrapperspb.String("notignored"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1235,11 +1245,12 @@ func TestCreateWorkerLed(t *testing.T) {
|
||||
},
|
||||
res: &pbs.CreateWorkerLedResponse{
|
||||
Item: &workers.Worker{
|
||||
ScopeId: scope.Global.String(),
|
||||
Name: &wrapperspb.StringValue{Value: "success"},
|
||||
Description: &wrapperspb.StringValue{Value: "success-description"},
|
||||
Version: 1,
|
||||
Type: PkiWorkerType,
|
||||
ScopeId: scope.Global.String(),
|
||||
Name: &wrapperspb.StringValue{Value: "success"},
|
||||
Description: &wrapperspb.StringValue{Value: "success-description"},
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
Version: 1,
|
||||
Type: PkiWorkerType,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user