feat(target): Add support for static credentials during authorize-session
This commit is contained in:
1 parent
c14fe05036
commit
4b365db634
11 files changed
+519
-236
No files matched your search
@@ -78,7 +78,7 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string {
|
||||
prefixStr := strings.Repeat(" ", indent)
|
||||
origSecret := []string{fmt.Sprintf("%s %s", prefixStr, sc.Secret.Raw)}
|
||||
switch sc.CredentialSource.Type {
|
||||
case "vault":
|
||||
case "vault", "static":
|
||||
if sc.Credential != nil {
|
||||
maxLength := 0
|
||||
for k := range sc.Credential {
|
||||
|
||||
@@ -888,7 +888,7 @@ func printCustomActionOutputImpl(c *Command) (bool, error) {
|
||||
|
||||
var secretStr []string
|
||||
switch cred.CredentialSource.Type {
|
||||
case "vault":
|
||||
case "vault", "static":
|
||||
switch {
|
||||
case cred.Credential != nil:
|
||||
maxLength := 0
|
||||
|
||||
@@ -175,7 +175,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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
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{
|
||||
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,
|
||||
}, 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
|
||||
}
|
||||
@@ -2,8 +2,6 @@ package targets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
@@ -23,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"
|
||||
@@ -44,7 +41,6 @@ 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"
|
||||
)
|
||||
@@ -97,6 +93,7 @@ type Service struct {
|
||||
pluginHostRepoFn common.PluginHostRepoFactory
|
||||
staticHostRepoFn common.StaticRepoFactory
|
||||
vaultCredRepoFn common.VaultCredentialRepoFactory
|
||||
staticCredRepoFn common.StaticCredentialRepoFactory
|
||||
kmsCache *kms.Kms
|
||||
}
|
||||
|
||||
@@ -111,6 +108,7 @@ func NewService(
|
||||
pluginHostRepoFn common.PluginHostRepoFactory,
|
||||
staticHostRepoFn common.StaticRepoFactory,
|
||||
vaultCredRepoFn common.VaultCredentialRepoFactory,
|
||||
staticCredRepoFn common.StaticCredentialRepoFactory,
|
||||
) (Service, error) {
|
||||
const op = "targets.NewService"
|
||||
if repoFn == nil {
|
||||
@@ -134,6 +132,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,
|
||||
@@ -142,6 +143,7 @@ func NewService(
|
||||
pluginHostRepoFn: pluginHostRepoFn,
|
||||
staticHostRepoFn: staticHostRepoFn,
|
||||
vaultCredRepoFn: vaultCredRepoFn,
|
||||
staticCredRepoFn: staticCredRepoFn,
|
||||
kmsCache: kmsCache,
|
||||
}, nil
|
||||
}
|
||||
@@ -873,7 +875,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())
|
||||
@@ -1049,14 +1051,22 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
|
||||
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()
|
||||
@@ -1073,6 +1083,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)
|
||||
@@ -1088,111 +1099,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)
|
||||
@@ -1226,7 +1208,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,
|
||||
@@ -1327,7 +1309,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 {
|
||||
@@ -1668,7 +1652,7 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou
|
||||
|
||||
// ApplicationCredentialLibrariesField is deprecated and should only be populated
|
||||
// for library type sources
|
||||
if cs.Type() == "library" {
|
||||
if cs.Type() == target.LibraryCredentialSourceType {
|
||||
appCredLibIds = append(appCredLibIds, cs.Id())
|
||||
appCredLibraries = append(appCredLibraries, &pb.CredentialLibrary{
|
||||
Id: cs.Id(),
|
||||
@@ -2168,22 +2152,3 @@ func validateAuthorizeSessionRequest(req *pbs.AuthorizeSessionRequest) error {
|
||||
}
|
||||
return 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))
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"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/daemon/controller/handlers/targets"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
@@ -39,7 +40,8 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
credpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries"
|
||||
credlibpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries"
|
||||
credpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
|
||||
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||
@@ -99,10 +101,13 @@ func testService(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapp
|
||||
pluginHostRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
||||
}
|
||||
credentialRepoFn := func() (*vault.Repository, error) {
|
||||
vaultCredRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
return targets.NewService(context.Background(), kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn)
|
||||
staticCredRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(context.Background(), rw, rw, kms)
|
||||
}
|
||||
return targets.NewService(context.Background(), kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
@@ -2436,8 +2441,8 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
resultLibIds []string
|
||||
}{
|
||||
{
|
||||
name: "Set libary on empty target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty libary"),
|
||||
name: "Set library on empty target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty library"),
|
||||
setCredentialSources: []string{cls[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{cls[1].GetPublicId()},
|
||||
resultLibIds: []string{cls[1].GetPublicId()},
|
||||
@@ -2450,14 +2455,14 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Set library on library populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated libary-libary", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})),
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated library-library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})),
|
||||
setCredentialSources: []string{cls[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{cls[1].GetPublicId()},
|
||||
resultLibIds: []string{cls[1].GetPublicId()},
|
||||
},
|
||||
{
|
||||
name: "Set static on library populated target",
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-libary", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})),
|
||||
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})),
|
||||
setCredentialSources: []string{creds[1].GetPublicId()},
|
||||
resultCredentialSourceIds: []string{creds[1].GetPublicId()},
|
||||
},
|
||||
@@ -2813,9 +2818,12 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
staticHostRepoFn := func() (*static.Repository, error) {
|
||||
return staticRepo, nil
|
||||
}
|
||||
credentialRepoFn := func() (*vault.Repository, error) {
|
||||
vaultCredRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
staticCredRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(ctx, rw, rw, kms)
|
||||
}
|
||||
atRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
@@ -2889,15 +2897,15 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
v.MountPKI(t)
|
||||
sec, tok := v.CreateToken(t, vault.WithPolicies([]string{"default", "boundary-controller", "pki"}))
|
||||
|
||||
store := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor)
|
||||
credService, err := credentiallibraries.NewService(credentialRepoFn, iamRepoFn)
|
||||
vaultStore := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor)
|
||||
credService, err := credentiallibraries.NewService(vaultCredRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("Library Name"),
|
||||
Description: wrapperspb.String("Library Description"),
|
||||
Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: &wrapperspb.StringValue{
|
||||
Value: path.Join("pki", "issue", "boundary"),
|
||||
},
|
||||
@@ -2943,7 +2951,7 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn)
|
||||
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn)
|
||||
require.NoError(t, err)
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -3013,14 +3021,14 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
Id: clsResp.GetItem().GetId(),
|
||||
Name: clsResp.GetItem().GetName().GetValue(),
|
||||
Description: clsResp.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: clsResp.GetItem().GetId(),
|
||||
Name: clsResp.GetItem().GetName().GetValue(),
|
||||
Description: clsResp.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
}},
|
||||
@@ -3085,9 +3093,12 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
staticHostRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
credentialRepoFn := func() (*vault.Repository, error) {
|
||||
vaultCredRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
staticCredRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(ctx, rw, rw, kms)
|
||||
}
|
||||
atRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
@@ -3112,7 +3123,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn)
|
||||
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
@@ -3124,20 +3135,20 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
v.AddKVPolicy(t)
|
||||
sec, tok := v.CreateToken(t, vault.WithPolicies([]string{"default", "boundary-controller", "secret"}))
|
||||
|
||||
store := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor)
|
||||
credService, err := credentiallibraries.NewService(credentialRepoFn, iamRepoFn)
|
||||
vaultStore := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor)
|
||||
credLibService, err := credentiallibraries.NewService(vaultCredRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create secret in vault with default username and password fields
|
||||
defaultUserPass := v.CreateKVSecret(t, "default-userpass", []byte(`{"data": {"username": "my-user", "password": "my-pass"}}`))
|
||||
require.NotNil(t, defaultUserPass)
|
||||
|
||||
clsRespUserPassword, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
clsRespUserPassword, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("Userpassword Library"),
|
||||
Description: wrapperspb.String("Userpassword Library Description"),
|
||||
Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "default-userpass")),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
@@ -3146,12 +3157,12 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
clsRespUnspecified, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
clsRespUnspecified, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("Unspecified Library"),
|
||||
Description: wrapperspb.String("Unspecified Library Description"),
|
||||
Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "default-userpass")),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
@@ -3163,12 +3174,12 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
nonDefaultUserPass := v.CreateKVSecret(t, "non-default-userpass", []byte(`{"data": {"non-default-user": "my-user", "non-default-pass": "my-pass"}}`))
|
||||
require.NotNil(t, nonDefaultUserPass)
|
||||
|
||||
clsRespUserPasswordWithMapping, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
clsRespUserPasswordWithMapping, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Name: wrapperspb.String("Userpassword Mapping Library"),
|
||||
Description: wrapperspb.String("Userpassword Mapping Library Description"),
|
||||
Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("secret", "data", "non-default-userpass")),
|
||||
HttpMethod: wrapperspb.String("GET"),
|
||||
},
|
||||
@@ -3181,55 +3192,146 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId())
|
||||
credService, err := credentials.NewService(staticCredRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
credResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
|
||||
CredentialStoreId: staticStore.GetPublicId(),
|
||||
Type: credstatic.UsernamePasswordSubtype.String(),
|
||||
Name: wrapperspb.String("Cred Name"),
|
||||
Description: wrapperspb.String("Cred Description"),
|
||||
Attrs: &credpb.Credential_UsernamePasswordAttributes{
|
||||
UsernamePasswordAttributes: &credpb.UsernamePasswordAttributes{
|
||||
Username: wrapperspb.String("static-username"),
|
||||
Password: wrapperspb.String("static-password"),
|
||||
},
|
||||
},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, credResp)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
hostSourceId string
|
||||
credLib *credpb.CredentialLibrary
|
||||
credSourceId string
|
||||
wantedHostId string
|
||||
wantedEndpoint string
|
||||
wantedCredType string
|
||||
wantedCred *structpb.Struct
|
||||
wantedCred *pb.SessionCredential
|
||||
}{
|
||||
{
|
||||
name: "unspecified",
|
||||
name: "vault-unspecified",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: clsRespUnspecified.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
credLib: clsRespUnspecified.GetItem(),
|
||||
wantedCred: &pb.SessionCredential{
|
||||
CredentialLibrary: &pb.CredentialLibrary{
|
||||
Id: clsRespUnspecified.GetItem().GetId(),
|
||||
Name: clsRespUnspecified.GetItem().GetName().GetValue(),
|
||||
Description: clsRespUnspecified.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: clsRespUnspecified.GetItem().GetId(),
|
||||
Name: clsRespUnspecified.GetItem().GetName().GetValue(),
|
||||
Description: clsRespUnspecified.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "userpassword",
|
||||
name: "vault-userpassword",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: clsRespUserPassword.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
credLib: clsRespUserPassword.GetItem(),
|
||||
wantedCred: func() *structpb.Struct {
|
||||
data := map[string]interface{}{
|
||||
"username": "my-user",
|
||||
"password": "my-pass",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
wantedCredType: string(credential.UsernamePasswordType),
|
||||
wantedCred: &pb.SessionCredential{
|
||||
CredentialLibrary: &pb.CredentialLibrary{
|
||||
Id: clsRespUserPassword.GetItem().GetId(),
|
||||
Name: clsRespUserPassword.GetItem().GetName().GetValue(),
|
||||
Description: clsRespUserPassword.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: clsRespUserPassword.GetItem().GetId(),
|
||||
Name: clsRespUserPassword.GetItem().GetName().GetValue(),
|
||||
Description: clsRespUserPassword.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
data := map[string]interface{}{
|
||||
"password": "my-pass",
|
||||
"username": "my-user",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "userpassword-with-mapping",
|
||||
name: "vault-userpassword-with-mapping",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: clsRespUserPasswordWithMapping.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
credLib: clsRespUserPasswordWithMapping.GetItem(),
|
||||
wantedCred: func() *structpb.Struct {
|
||||
data := map[string]interface{}{
|
||||
"password": "my-pass",
|
||||
"username": "my-user",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
wantedCredType: string(credential.UsernamePasswordType),
|
||||
wantedCred: &pb.SessionCredential{
|
||||
CredentialLibrary: &pb.CredentialLibrary{
|
||||
Id: clsRespUserPasswordWithMapping.GetItem().GetId(),
|
||||
Name: clsRespUserPasswordWithMapping.GetItem().GetName().GetValue(),
|
||||
Description: clsRespUserPasswordWithMapping.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: clsRespUserPasswordWithMapping.GetItem().GetId(),
|
||||
Name: clsRespUserPasswordWithMapping.GetItem().GetName().GetValue(),
|
||||
Description: clsRespUserPasswordWithMapping.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: vaultStore.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
data := map[string]interface{}{
|
||||
"password": "my-pass",
|
||||
"username": "my-user",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "static-userpassword",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: credResp.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
wantedCred: &pb.SessionCredential{
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: credResp.GetItem().GetId(),
|
||||
Name: credResp.GetItem().GetName().GetValue(),
|
||||
Description: credResp.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: staticStore.GetPublicId(),
|
||||
Type: credstatic.Subtype.String(),
|
||||
CredentialType: string(credential.UsernamePasswordType),
|
||||
},
|
||||
Credential: func() *structpb.Struct {
|
||||
data := map[string]interface{}{
|
||||
"password": "static-password",
|
||||
"username": "static-username",
|
||||
}
|
||||
st, err := structpb.NewStruct(data)
|
||||
require.NoError(t, err)
|
||||
return st
|
||||
}(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3246,7 +3348,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
_, err = s.AddTargetCredentialSources(ctx,
|
||||
&pbs.AddTargetCredentialSourcesRequest{
|
||||
Id: tar.GetPublicId(),
|
||||
ApplicationCredentialSourceIds: []string{tc.credLib.GetId()},
|
||||
ApplicationCredentialSourceIds: []string{tc.credSourceId},
|
||||
Version: apiTar.GetItem().GetVersion(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -3274,32 +3376,13 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
Description: proj.GetDescription(),
|
||||
ParentScopeId: proj.GetParentId(),
|
||||
},
|
||||
TargetId: tar.GetPublicId(),
|
||||
UserId: at.GetIamUserId(),
|
||||
HostSetId: tc.hostSourceId,
|
||||
HostId: tc.wantedHostId,
|
||||
Type: "tcp",
|
||||
Endpoint: fmt.Sprintf("tcp://%s:%d", tc.wantedEndpoint, defaultPort),
|
||||
Credentials: []*pb.SessionCredential{
|
||||
{
|
||||
CredentialLibrary: &pb.CredentialLibrary{
|
||||
Id: tc.credLib.GetId(),
|
||||
Name: tc.credLib.GetName().GetValue(),
|
||||
Description: tc.credLib.GetDescription().GetValue(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: tc.credLib.GetId(),
|
||||
Name: tc.credLib.GetName().GetValue(),
|
||||
Description: tc.credLib.GetDescription().GetValue(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
CredentialType: tc.wantedCredType,
|
||||
},
|
||||
Credential: tc.wantedCred,
|
||||
},
|
||||
},
|
||||
TargetId: tar.GetPublicId(),
|
||||
UserId: at.GetIamUserId(),
|
||||
HostSetId: tc.hostSourceId,
|
||||
HostId: tc.wantedHostId,
|
||||
Type: "tcp",
|
||||
Endpoint: fmt.Sprintf("tcp://%s:%d", tc.wantedEndpoint, defaultPort),
|
||||
Credentials: []*pb.SessionCredential{tc.wantedCred},
|
||||
// TODO: validate the contents of the authorization token is what is expected
|
||||
}
|
||||
got := asRes.GetItem()
|
||||
@@ -3350,15 +3433,18 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
pluginHostRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
||||
}
|
||||
credentialRepoFn := func() (*vault.Repository, error) {
|
||||
vaultCredRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
staticCredRepoFn := func() (*credstatic.Repository, error) {
|
||||
return credstatic.NewRepository(ctx, rw, rw, kms)
|
||||
}
|
||||
atRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn)
|
||||
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Authorized user gets full permissions
|
||||
@@ -3441,13 +3527,13 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
}
|
||||
|
||||
libraryExists := func(tar target.Target) (version uint32) {
|
||||
credService, err := credentiallibraries.NewService(credentialRepoFn, iamRepoFn)
|
||||
credService, err := credentiallibraries.NewService(vaultCredRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{
|
||||
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Description: wrapperspb.String(fmt.Sprintf("Library Description for target %q", tar.GetName())),
|
||||
Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String(path.Join("database", "creds", "opened")),
|
||||
},
|
||||
},
|
||||
@@ -3465,13 +3551,13 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
}
|
||||
|
||||
misConfiguredlibraryExists := func(tar target.Target) (version uint32) {
|
||||
credService, err := credentiallibraries.NewService(credentialRepoFn, iamRepoFn)
|
||||
credService, err := credentiallibraries.NewService(vaultCredRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{
|
||||
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Description: wrapperspb.String(fmt.Sprintf("Library Description for target %q", tar.GetName())),
|
||||
Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{
|
||||
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
||||
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
||||
Path: wrapperspb.String("bad path"),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -40,7 +40,8 @@ begin;
|
||||
tcl.target_id,
|
||||
tcl.credential_library_id as credential_source_id,
|
||||
tcl.credential_purpose,
|
||||
cl.store_id
|
||||
cl.store_id,
|
||||
'library' as type
|
||||
from
|
||||
target_credential_library tcl,
|
||||
credential_library cl
|
||||
@@ -51,7 +52,8 @@ begin;
|
||||
tcs.target_id,
|
||||
tcs.credential_static_id as credential_source_id,
|
||||
tcs.credential_purpose,
|
||||
cst.store_id
|
||||
cst.store_id,
|
||||
'static' as type
|
||||
from
|
||||
target_static_credential tcs,
|
||||
credential_static cst
|
||||
|
||||
@@ -125,6 +125,10 @@ message CredentialSource {
|
||||
// create_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
timestamp.v1.Timestamp create_time = 40;
|
||||
|
||||
// type of credential source (library or static)
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string type = 50;
|
||||
}
|
||||
|
||||
message CredentialSourceView {
|
||||
|
||||
@@ -5,6 +5,15 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/target/store"
|
||||
)
|
||||
|
||||
// CredentialSourceType is the type of credential source.
|
||||
type CredentialSourceType string
|
||||
|
||||
// Credential source type values.
|
||||
const (
|
||||
LibraryCredentialSourceType CredentialSourceType = "library"
|
||||
StaticCredentialSourceType CredentialSourceType = "static"
|
||||
)
|
||||
|
||||
var _ CredentialSource = (*TargetCredentialSource)(nil)
|
||||
|
||||
// CredentialSource is an interface that can be implemented by both a library
|
||||
@@ -14,10 +23,11 @@ type CredentialSource interface {
|
||||
Id() string
|
||||
CredentialPurpose() credential.Purpose
|
||||
TargetId() string
|
||||
Type() CredentialSourceType
|
||||
}
|
||||
|
||||
// CredentialSources contains slices of credential publicIds
|
||||
// per purpose to be attacehd to the target.
|
||||
// per purpose to be attached to the target.
|
||||
type CredentialSources struct {
|
||||
ApplicationCredentialIds []string
|
||||
EgressCredentialIds []string
|
||||
@@ -58,6 +68,11 @@ func (ts *TargetCredentialSource) TargetId() string {
|
||||
return ts.GetTargetId()
|
||||
}
|
||||
|
||||
// Type returns the type of the credential source (library or static)
|
||||
func (ts *TargetCredentialSource) Type() CredentialSourceType {
|
||||
return CredentialSourceType(ts.GetType())
|
||||
}
|
||||
|
||||
// credentialSourceView provides a common way to return credential sources regardless of their
|
||||
// underlying type (library or static).
|
||||
type credentialSourceView struct {
|
||||
|
||||
@@ -478,8 +478,8 @@ func (r *Repository) changes(ctx context.Context, targetId string, ids []string,
|
||||
if err := r.reader.ScanRows(ctx, rows, &chg); err != nil {
|
||||
return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("scan row failed"))
|
||||
}
|
||||
switch chg.Type {
|
||||
case "library":
|
||||
switch CredentialSourceType(chg.Type) {
|
||||
case LibraryCredentialSourceType:
|
||||
lib, err := NewCredentialLibrary(targetId, chg.SourceId, purpose)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, errors.Wrap(ctx, err, op)
|
||||
@@ -490,7 +490,7 @@ func (r *Repository) changes(ctx context.Context, targetId string, ids []string,
|
||||
default:
|
||||
addCredLib = append(addCredLib, lib)
|
||||
}
|
||||
case "static":
|
||||
case StaticCredentialSourceType:
|
||||
cred, err := NewStaticCredential(targetId, chg.SourceId, purpose)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, errors.Wrap(ctx, err, op)
|
||||
@@ -547,9 +547,9 @@ func (r *Repository) createSources(ctx context.Context, tId string, tSubtype sub
|
||||
|
||||
// Create a map between credential source ID and it's type (library or static).
|
||||
// This will allow for a quick lookup when calling the corresponding New below
|
||||
credTypeById := make(map[string]string, len(ids))
|
||||
credTypeById := make(map[string]CredentialSourceType, len(ids))
|
||||
for _, cv := range credView {
|
||||
credTypeById[cv.GetPublicId()] = cv.GetType()
|
||||
credTypeById[cv.GetPublicId()] = CredentialSourceType(cv.GetType())
|
||||
}
|
||||
|
||||
credLibs := make([]*CredentialLibrary, 0, totalCreds)
|
||||
@@ -561,13 +561,13 @@ func (r *Repository) createSources(ctx context.Context, tId string, tSubtype sub
|
||||
for purpose, ids := range byPurpose {
|
||||
for _, id := range ids {
|
||||
switch credTypeById[id] {
|
||||
case "library":
|
||||
case LibraryCredentialSourceType:
|
||||
lib, err := NewCredentialLibrary(tId, id, purpose)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
credLibs = append(credLibs, lib)
|
||||
case "static":
|
||||
case StaticCredentialSourceType:
|
||||
cred, err := NewStaticCredential(tId, id, purpose)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(ctx, err, op)
|
||||
|
||||
@@ -427,6 +427,9 @@ type CredentialSource struct {
|
||||
// create_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,40,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// type of credential source (library or static)
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
Type string `protobuf:"bytes,50,opt,name=type,proto3" json:"type,omitempty" gorm:"not_null"`
|
||||
}
|
||||
|
||||
func (x *CredentialSource) Reset() {
|
||||
@@ -489,6 +492,13 @@ func (x *CredentialSource) GetCreateTime() *timestamp.Timestamp {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CredentialSource) GetType() string {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CredentialSourceView struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -628,7 +638,7 @@ var file_controller_storage_target_store_v1_target_proto_rawDesc = []byte{
|
||||
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22,
|
||||
0xdd, 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f,
|
||||
0xf1, 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49,
|
||||
0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f,
|
||||
@@ -641,16 +651,18 @@ var file_controller_storage_target_store_v1_target_proto_rawDesc = []byte{
|
||||
0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
|
||||
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22,
|
||||
0x47, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69,
|
||||
0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c,
|
||||
0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x14, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70,
|
||||
0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74,
|
||||
0x79, 0x70, 0x65, 0x22, 0x47, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x70,
|
||||
0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||
0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x42, 0x3b, 0x5a, 0x39,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69,
|
||||
0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x73, 0x74,
|
||||
0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
Reference in new issue
Block a user